Skip to content

Commit 71d283b

Browse files
committed
[libc] add a few tests to handles edge cases with FRACT_{MIN/MAX}
Signed-off-by: Shreeyash Pandey <[email protected]>
1 parent 040e833 commit 71d283b

File tree

2 files changed

+37
-20
lines changed

2 files changed

+37
-20
lines changed

libc/src/__support/fixed_point/fx_bits.h

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,10 @@ template <typename XType> LIBC_INLINE constexpr XType divi(int n, int d) {
243243
if (LIBC_UNLIKELY(n == 0)) {
244244
return FXRep<XType>::ZERO();
245245
}
246-
bool d_is_signed = false;
247-
if (d < 0) {
248-
d = (d * -1);
249-
d_is_signed = true;
250-
}
246+
bool result_is_negative = (n < 0) ^ (d < 0);
251247

252-
unsigned int nv = static_cast<unsigned int>(n);
253-
unsigned int dv = static_cast<unsigned int>(d);
248+
unsigned int nv = static_cast<unsigned int>(n < 0 ? -n : n);
249+
unsigned int dv = static_cast<unsigned int>(d < 0 ? -d : d);
254250
unsigned int clz = cpp::countl_zero<unsigned int>(dv) - 1;
255251
unsigned long int scaled_val = dv << clz;
256252
/* Scale denominator to be in the range of [0.5,1] */
@@ -281,11 +277,25 @@ template <typename XType> LIBC_INLINE constexpr XType divi(int n, int d) {
281277
val = nrstep(d_scaled_val, val);
282278
/* E4 = 2.155e−20 */
283279
val = nrstep(d_scaled_val, val);
280+
284281
long accum res = n_scaled_val * val;
285-
if (d_is_signed) {
286-
res *= static_cast<XType>(-1);
282+
283+
if (result_is_negative) {
284+
res *= static_cast<long accum>(-1);
285+
}
286+
287+
// Check for overflow before returning
288+
long accum max_val = static_cast<long accum>(FXRep<XType>::MAX());
289+
long accum min_val = static_cast<long accum>(FXRep<XType>::MIN());
290+
291+
/* Per clause 7.18a.6.1, saturate values on overflow */
292+
if (res > max_val) {
293+
return FXRep<XType>::MAX();
294+
} else if (res < min_val) {
295+
return FXRep<XType>::MIN();
296+
} else {
297+
return static_cast<XType>(res);
287298
}
288-
return static_cast<XType>(res);
289299
}
290300

291301
} // namespace fixed_point

libc/test/src/stdfix/DivITest.h

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include "src/__support/fixed_point/fx_rep.h"
1212
#include "test/UnitTest/Test.h"
1313

14+
#include <stdio.h>
15+
1416
template <typename XType> XType get_epsilon() = delete;
1517
template <> fract get_epsilon() { return FRACT_EPSILON; }
1618
template <> unsigned fract get_epsilon() { return UFRACT_EPSILON; }
@@ -44,22 +46,27 @@ class DivITest : public LIBC_NAMESPACE::testing::Test {
4446
}
4547

4648
void testSpecial(DivIFunc func) {
47-
EXPECT_EQ(func(0,10), 0.r);
48-
EXPECT_EQ(func(0,-10), 0.r);
49-
EXPECT_EQ(func(-32768,32768), FRACT_MIN);
50-
EXPECT_EQ(func(32767,32768), FRACT_MAX);
51-
EXPECT_EQ(func(INT_MAX,INT_MAX), 1.0r);
52-
EXPECT_EQ(func(INT_MAX-1,INT_MAX), 0.99999999r);
53-
EXPECT_EQ(func(INT_MIN,INT_MAX), FRACT_MIN);
54-
/* Expecting 0 here as fract is not precise enough to
49+
XType epsilon = get_epsilon<XType>();
50+
EXPECT_EQ(func(0, 10), 0.r);
51+
EXPECT_EQ(func(0, -10), 0.r);
52+
EXPECT_EQ(func(-(1 << FRACT_FBIT), 1 << FRACT_FBIT), FRACT_MIN);
53+
EXPECT_EQ(func((1 << FRACT_FBIT) - 1, 1 << FRACT_FBIT), FRACT_MAX);
54+
/* From Section 7.18a.6.1, functions returning a fixed-point value, the
55+
* return value is saturated on overflow. */
56+
EXPECT_EQ(func(INT_MAX, INT_MAX), FRACT_MAX);
57+
EXPECT_LT(func(INT_MAX - 1, INT_MAX) - 0.99999999r, epsilon);
58+
EXPECT_EQ(func(INT_MIN, INT_MAX), FRACT_MIN);
59+
/* Expecting 0 here as fract is not precise enough to
5560
* handle 1/INT_MAX
5661
*/
57-
EXPECT_EQ(func(1, INT_MAX), 0.r);
62+
EXPECT_LT(func(1, INT_MAX) - 0.r, epsilon);
63+
/* This results in 1.1739, which should be saturated to FRACT_MAX */
64+
EXPECT_EQ(func(27, 23), FRACT_MAX);
5865
}
5966
};
6067

6168
#define LIST_DIVI_TESTS(Name, XType, func) \
6269
using LlvmLibc##Name##diviTest = DivITest<XType>; \
6370
TEST_F(LlvmLibc##Name##diviTest, Basic) { testBasic(&func); } \
64-
TEST_F(LlvmLibc##Name##diviTest, Special) { testSpecial(&func); } \
71+
TEST_F(LlvmLibc##Name##diviTest, Special) { testSpecial(&func); } \
6572
static_assert(true, "Require semicolon.")

0 commit comments

Comments
 (0)