Skip to content

Commit 92dfe20

Browse files
authored
[libc][math] Fix exact cases for powf. (#91488)
It was reported from the CORE-MATH project that the `powf` implementation did not round correctly when `x^y` is either exact for exactly half-way. This PR deals with the potential exact cases when `y` is an integer `2 < y < 25`. In such cases, the results of `x^y` is exactly representable in double precision.
1 parent c5c8ae4 commit 92dfe20

File tree

2 files changed

+34
-8
lines changed

2 files changed

+34
-8
lines changed

Diff for: libc/src/math/generic/powf.cpp

+21-2
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ LLVM_LIBC_FUNCTION(float, powf, (float x, float y)) {
528528
// So if |y| > 151 * 2^24, and x is finite:
529529
// |y * log2(x)| = 0 or > 151.
530530
// Hence x^y will either overflow or underflow if x is not zero.
531-
if (LIBC_UNLIKELY((y_abs & 0x007f'ffff) == 0) || (y_abs > 0x4f170000)) {
531+
if (LIBC_UNLIKELY((y_abs & 0x0007'ffff) == 0) || (y_abs > 0x4f170000)) {
532532
// Exceptional exponents.
533533
switch (y_abs) {
534534
case 0x0000'0000: { // y = +-0.0f
@@ -572,6 +572,26 @@ LLVM_LIBC_FUNCTION(float, powf, (float x, float y)) {
572572
// case 0xbf00'0000: // pow(x, -1/2) = rsqrt(x)
573573
// return rsqrt(x);
574574
}
575+
if (is_integer(y) && (y_u > 0x4000'0000) && (y_u <= 0x41c0'0000)) {
576+
// Check for exact cases when 2 < y < 25 and y is an integer.
577+
int msb =
578+
(x_abs == 0) ? (FloatBits::TOTAL_LEN - 2) : cpp::countl_zero(x_abs);
579+
msb = (msb > FloatBits::EXP_LEN) ? msb : FloatBits::EXP_LEN;
580+
int lsb = (x_abs == 0) ? 0 : cpp::countr_zero(x_abs);
581+
lsb = (lsb > FloatBits::FRACTION_LEN) ? FloatBits::FRACTION_LEN : lsb;
582+
int extra_bits = FloatBits::TOTAL_LEN - 2 - lsb - msb;
583+
int iter = static_cast<int>(y);
584+
585+
if (extra_bits * iter <= FloatBits::FRACTION_LEN + 2) {
586+
// The result is either exact or exactly half-way.
587+
// But it is exactly representable in double precision.
588+
double x_d = static_cast<double>(x);
589+
double result = x_d;
590+
for (int i = 1; i < iter; ++i)
591+
result *= x_d;
592+
return static_cast<float>(result);
593+
}
594+
}
575595
if (y_abs > 0x4f17'0000) {
576596
if (y_abs > 0x7f80'0000) {
577597
// y is NaN
@@ -834,7 +854,6 @@ LLVM_LIBC_FUNCTION(float, powf, (float x, float y)) {
834854
return static_cast<float>(
835855
powf_double_double(idx_x, dx, y6, lo6_hi, exp2_hi_mid_dd)) +
836856
0.0f;
837-
// return static_cast<float>(r);
838857
}
839858

840859
} // namespace LIBC_NAMESPACE

Diff for: libc/test/src/math/powf_test.cpp

+13-6
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,21 @@ using LIBC_NAMESPACE::testing::tlog;
2222
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
2323

2424
TEST_F(LlvmLibcPowfTest, TrickyInputs) {
25-
constexpr int N = 11;
25+
constexpr int N = 13;
2626
constexpr mpfr::BinaryInput<float> INPUTS[N] = {
27-
{0x1.290bbp-124f, 0x1.1e6d92p-25f}, {0x1.2e9fb6p+5f, -0x1.1b82b6p-18f},
28-
{0x1.6877f6p+60f, -0x1.75f1c6p-4f}, {0x1.0936acp-63f, -0x1.55200ep-15f},
29-
{0x1.d6d72ap+43f, -0x1.749ccap-5f}, {0x1.4afb2ap-40f, 0x1.063198p+0f},
30-
{0x1.0124dep+0f, -0x1.fdb016p+9f}, {0x1.1058p+0f, 0x1.ap+64f},
31-
{0x1.1058p+0f, -0x1.ap+64f}, {0x1.1058p+0f, 0x1.ap+64f},
27+
{0x1.290bbp-124f, 0x1.1e6d92p-25f},
28+
{0x1.2e9fb6p+5f, -0x1.1b82b6p-18f},
29+
{0x1.6877f6p+60f, -0x1.75f1c6p-4f},
30+
{0x1.0936acp-63f, -0x1.55200ep-15f},
31+
{0x1.d6d72ap+43f, -0x1.749ccap-5f},
32+
{0x1.4afb2ap-40f, 0x1.063198p+0f},
33+
{0x1.0124dep+0f, -0x1.fdb016p+9f},
34+
{0x1.1058p+0f, 0x1.ap+64f},
35+
{0x1.1058p+0f, -0x1.ap+64f},
36+
{0x1.1058p+0f, 0x1.ap+64f},
3237
{0x1.fa32d4p-1f, 0x1.67a62ep+12f},
38+
{-0x1.8p-49, 0x1.8p+1},
39+
{0x1.8p-48, 0x1.8p+1},
3340
};
3441

3542
for (int i = 0; i < N; ++i) {

0 commit comments

Comments
 (0)