Skip to content

Commit 7dd4ce4

Browse files
authored
[libc][stdfix] Fix overflow problem for fixed point sqrt when the inputs are close to max. (#90558)
Fixes #89668
1 parent 600cae7 commit 7dd4ce4

File tree

4 files changed

+37
-14
lines changed

4 files changed

+37
-14
lines changed

libc/src/__support/fixed_point/sqrt.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,22 @@ template <> struct SqrtConfig<unsigned fract> {
5555
// Linear approximation for the initial values, with errors bounded by:
5656
// max(1.5 * 2^-11, eps)
5757
// Generated with Sollya:
58-
// > for i from 4 to 15 do {
58+
// > for i from 4 to 14 do {
5959
// P = fpminimax(sqrt(x), 1, [|16, 16|], [i * 2^-4, (i + 1)*2^-4],
6060
// fixed, absolute);
6161
// print("{", coeff(P, 1), "ur,", coeff(P, 0), "ur},");
6262
// };
63+
// For the last interval [15/16, 1), we choose the linear function Q such that
64+
// Q(1) = 1 and Q(15/16) = P(15/16),
65+
// where P is the polynomial generated by Sollya above for [14/16, 15/16].
66+
// This is to prevent overflow in the last interval [15/16, 1).
6367
static constexpr Type FIRST_APPROX[12][2] = {
6468
{0x1.e378p-1ur, 0x1.0ebp-2ur}, {0x1.b512p-1ur, 0x1.2b94p-2ur},
6569
{0x1.91fp-1ur, 0x1.45dcp-2ur}, {0x1.7622p-1ur, 0x1.5e24p-2ur},
6670
{0x1.5f5ap-1ur, 0x1.74e4p-2ur}, {0x1.4c58p-1ur, 0x1.8a4p-2ur},
6771
{0x1.3c1ep-1ur, 0x1.9e84p-2ur}, {0x1.2e0cp-1ur, 0x1.b1d8p-2ur},
6872
{0x1.21aap-1ur, 0x1.c468p-2ur}, {0x1.16bap-1ur, 0x1.d62cp-2ur},
69-
{0x1.0cfp-1ur, 0x1.e74cp-2ur}, {0x1.0418p-1ur, 0x1.f7ep-2ur},
73+
{0x1.0cfp-1ur, 0x1.e74cp-2ur}, {0x1.039p-1ur, 0x1.f8ep-2ur},
7074
};
7175
};
7276

@@ -77,11 +81,15 @@ template <> struct SqrtConfig<unsigned long fract> {
7781
// Linear approximation for the initial values, with errors bounded by:
7882
// max(1.5 * 2^-11, eps)
7983
// Generated with Sollya:
80-
// > for i from 4 to 15 do {
84+
// > for i from 4 to 14 do {
8185
// P = fpminimax(sqrt(x), 1, [|32, 32|], [i * 2^-4, (i + 1)*2^-4],
8286
// fixed, absolute);
8387
// print("{", coeff(P, 1), "ulr,", coeff(P, 0), "ulr},");
8488
// };
89+
// For the last interval [15/16, 1), we choose the linear function Q such that
90+
// Q(1) = 1 and Q(15/16) = P(15/16),
91+
// where P is the polynomial generated by Sollya above for [14/16, 15/16].
92+
// This is to prevent overflow in the last interval [15/16, 1).
8593
static constexpr Type FIRST_APPROX[12][2] = {
8694
{0x1.e3779b98p-1ulr, 0x1.0eaff788p-2ulr},
8795
{0x1.b5167872p-1ulr, 0x1.2b908ad4p-2ulr},
@@ -94,7 +102,7 @@ template <> struct SqrtConfig<unsigned long fract> {
94102
{0x1.21b05c0ap-1ulr, 0x1.c45e023p-2ulr},
95103
{0x1.16becd02p-1ulr, 0x1.d624031p-2ulr},
96104
{0x1.0cf49fep-1ulr, 0x1.e743b844p-2ulr},
97-
{0x1.04214e9cp-1ulr, 0x1.f7ce2c3cp-2ulr},
105+
{0x1.038cdfcp-1ulr, 0x1.f8e6408p-2ulr},
98106
};
99107
};
100108

libc/test/src/stdfix/ISqrtTest.h

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ template <typename T> class ISqrtTest : public LIBC_NAMESPACE::testing::Test {
2727
public:
2828
typedef OutType (*SqrtFunc)(T);
2929

30+
void testSpecificInput(T input, OutType result, double expected,
31+
double tolerance) {
32+
double y_d = static_cast<double>(result);
33+
double errors = LIBC_NAMESPACE::fputil::abs((y_d / expected) - 1.0);
34+
if (errors > tolerance) {
35+
// Print out the failure input and output.
36+
EXPECT_EQ(input, T(0));
37+
EXPECT_EQ(result, zero);
38+
}
39+
ASSERT_TRUE(errors <= tolerance);
40+
}
41+
3042
void testSpecialNumbers(SqrtFunc func) {
3143
EXPECT_EQ(zero, func(T(0)));
3244

@@ -42,15 +54,9 @@ template <typename T> class ISqrtTest : public LIBC_NAMESPACE::testing::Test {
4254
for (int i = 0; i < COUNT; ++i) {
4355
x_d += 1.0;
4456
++x;
45-
double y_d = static_cast<double>(func(x));
46-
double result = LIBC_NAMESPACE::fputil::sqrt(x_d);
47-
double errors = LIBC_NAMESPACE::fputil::abs((y_d / result) - 1.0);
48-
if (errors > ERR) {
49-
// Print out the failure input and output.
50-
EXPECT_EQ(x, T(0));
51-
EXPECT_EQ(func(x), zero);
52-
}
53-
ASSERT_TRUE(errors <= ERR);
57+
OutType result = func(x);
58+
double expected = LIBC_NAMESPACE::fputil::sqrt(x_d);
59+
testSpecificInput(x, result, expected, ERR);
5460
}
5561
}
5662
};

libc/test/src/stdfix/SqrtTest.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ template <typename T> class SqrtTest : public LIBC_NAMESPACE::testing::Test {
4242

4343
constexpr size_t COUNT = 255;
4444
constexpr StorageType STEP =
45-
~StorageType(0) / static_cast<StorageType>(COUNT);
45+
StorageType(~StorageType(0)) / static_cast<StorageType>(COUNT);
4646
constexpr double ERR = 3.0 * static_cast<double>(eps);
4747
StorageType x = 0;
4848
for (size_t i = 0; i < COUNT; ++i, x += STEP) {

libc/test/src/stdfix/uksqrtui_test.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,13 @@ unsigned accum uksqrtui_fast(unsigned int x) {
1717

1818
LIST_ISQRT_TESTS(UI, unsigned int, LIBC_NAMESPACE::uksqrtui);
1919

20+
TEST_F(LlvmLibcISqrtUITest, LargeInteger) {
21+
testSpecificInput(65529u, LIBC_NAMESPACE::uksqrtui(65529u), 0x1.fff8fep7,
22+
0x3.0p-16);
23+
}
24+
2025
LIST_ISQRT_TESTS(UIFast, unsigned int, uksqrtui_fast);
26+
27+
TEST_F(LlvmLibcISqrtUIFastTest, LargeInteger) {
28+
testSpecificInput(65529u, uksqrtui_fast(65529u), 0x1.fff8fep7, 0x3.0p-16);
29+
}

0 commit comments

Comments
 (0)