Skip to content

Commit ccc2765

Browse files
committed
[libc] Add a new version log_eval.
1 parent d67401a commit ccc2765

File tree

4 files changed

+68
-57
lines changed

4 files changed

+68
-57
lines changed

libc/src/math/generic/atanhf16.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,13 @@ LLVM_LIBC_FUNCTION(float16, atanhf16, (float16 x)) {
4444

4545
// |x| >= 1
4646
if (LIBC_UNLIKELY(x_abs >= 0x3c00U)) {
47-
if (xbits.is_nan())
47+
if (xbits.is_nan()) {
48+
if (xbits.is_signaling_nan()) {
49+
fputil::raise_except_if_required(FE_INVALID);
50+
return FPBits::quiet_nan().get_val();
51+
}
4852
return x;
53+
}
4954

5055
// |x| == 1.0
5156
if (x_abs == 0x3c00U) {
@@ -93,7 +98,7 @@ LLVM_LIBC_FUNCTION(float16, atanhf16, (float16 x)) {
9398
}
9499

95100
float xf = x;
96-
return fputil::cast<float16>(0.5 * log_eval_f((xf + 1.0f) / (xf - 1.0f)));
101+
return fputil::cast<float16>(0.5 * log_eval((xf + 1.0f) / (xf - 1.0f)));
97102
}
98103

99104
} // namespace LIBC_NAMESPACE_DECL

libc/src/math/generic/common_constants.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ namespace LIBC_NAMESPACE_DECL {
1515

1616
// Lookup table for logf(f) = logf(1 + n*2^(-7)) where n = 0..127,
1717
// computed and stored as float precision constants.
18+
// Generated by Sollya with the following commands:
19+
// display = hexadecimal;
20+
// for n from 0 to 127 do { print(single(1 / (1 + n / 128.0))); };
1821
const float ONE_OVER_F_FLOAT[128] = {
1922
0x1p0f, 0x1.fc07fp-1f, 0x1.f81f82p-1f, 0x1.f4465ap-1f,
2023
0x1.f07c2p-1f, 0x1.ecc07cp-1f, 0x1.e9131ap-1f, 0x1.e573acp-1f,
@@ -97,6 +100,9 @@ const double ONE_OVER_F[128] = {
97100

98101
// Lookup table for (1/f) where f = 1 + n*2^(-7), n = 0..127,
99102
// computed and stored as float precision constants.
103+
// Generated by Sollya with the following commands:
104+
// display = hexadecimal;
105+
// for n from 0 to 127 do { print(single(log(1 + n / 128.0))); };
100106
const float LOG_F_FLOAT[128] = {
101107
0.0f, 0x1.fe02a6p-8f, 0x1.fc0a8cp-7f, 0x1.7b91bp-6f,
102108
0x1.f829bp-6f, 0x1.39e87cp-5f, 0x1.77459p-5f, 0x1.b42dd8p-5f,

libc/src/math/generic/explogxf.h

Lines changed: 51 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,29 @@ struct exp_b_reduc_t {
133133
double lo;
134134
};
135135

136+
// Coefficients for double (6th-degree minimax polynomial on [0, 2^-7]).
137+
// Minimax polynomial of log(1 + dx) generated by Sollya with:
138+
// > P = fpminimax(log(1 + x)/x, 6, [|D...|], [0, 2^-7]);
139+
static constexpr double LOG_COEFFS_DOUBLE[6] = {
140+
-0x1.ffffffffffffcp-2, 0x1.5555555552ddep-2, -0x1.ffffffefe562dp-3,
141+
0x1.9999817d3a50fp-3, -0x1.554317b3f67a5p-3, 0x1.1dc5c45e09c18p-3};
142+
143+
// Coefficients for float (6th-degree minimax polynomial on [0, 2^-7]).
144+
// Minimax polynomial of log(1 + dx) generated by Sollya with:
145+
// > P = fpminimax(log(1 + x)/x, 6, [|D...|], [0, 2^-7]);
146+
static constexpr float LOG_COEFFS_FLOAT[6] = {-0x1.fffffep-2f, 0x1.555556p-2f,
147+
-0x1.fffefep-3f, 0x1.99999ap-3f,
148+
-0x1.554318p-3f, 0x1.1dc5c4p-3f};
149+
150+
// log(2) in double precision.
151+
static constexpr double LOG2_DOUBLE = 0x1.62e42fefa39efp-1;
152+
153+
// log(2) in float precision.
154+
// Generated by Sollya with the following commands:
155+
// > display = hexadecimal;
156+
// > round(log(2), SG, RN);
157+
static constexpr float LOG2_FLOAT = 0x1.62e43p-1f;
158+
136159
// The function correctly calculates b^x value with at least float precision
137160
// in a limited range.
138161
// Range reduction:
@@ -297,73 +320,46 @@ LIBC_INLINE static double log2_eval(double x) {
297320
return result;
298321
}
299322

300-
// x should be positive, normal finite value
301-
LIBC_INLINE static float log_eval_f(float x) {
323+
template <typename T> LIBC_INLINE static T log_eval(T x) {
302324
// For x = 2^ex * (1 + mx), logf(x) = ex * logf(2) + logf(1 + mx).
303-
using FPB = fputil::FPBits<float>;
304-
FPB bs(x);
325+
using FPBits = fputil::FPBits<T>;
326+
FPBits xbits(x);
305327

306-
float ex = static_cast<float>(bs.get_exponent());
328+
T ex = static_cast<T>(xbits.get_exponent());
307329
// p1 is the leading 7 bits of mx, i.e.
308330
// p1 * 2^(-7) <= m_x < (p1 + 1) * 2^(-7).
309-
int p1 = static_cast<int>(bs.get_mantissa() >> (FPB::FRACTION_LEN - 7));
331+
int p1 = static_cast<int>(xbits.get_mantissa() >> (FPBits::FRACTION_LEN - 7));
310332

311333
// Set bs to (1 + (mx - p1*2^(-7))
312-
bs.set_uintval(bs.uintval() & (FPB::FRACTION_MASK >> 7));
313-
bs.set_biased_exponent(FPB::EXP_BIAS);
334+
xbits.set_uintval(xbits.uintval() & (FPBits::FRACTION_MASK >> 7));
335+
xbits.set_biased_exponent(FPBits::EXP_BIAS);
314336
// dx = (mx - p1*2^(-7)) / (1 + p1*2^(-7)).
315-
float dx = (bs.get_val() - 1.0f) * ONE_OVER_F_FLOAT[p1];
316-
317-
// Minimax polynomial of log(1 + dx) generated by Sollya with:
318-
// > P = fpminimax(log(1 + x)/x, 6, [|D...|], [0, 2^-7]);
319-
const float COEFFS[6] = {-0x1.fffffep-2f, 0x1.555556p-2f, -0x1.fffefep-3f,
320-
0x1.99999ap-3f, -0x1.554318p-3f, 0x1.1dc5c4p-3f};
321-
322-
float dx2 = dx * dx;
323-
324-
float c1 = fputil::multiply_add(dx, COEFFS[1], COEFFS[0]);
325-
float c2 = fputil::multiply_add(dx, COEFFS[3], COEFFS[2]);
326-
float c3 = fputil::multiply_add(dx, COEFFS[5], COEFFS[4]);
337+
T dx = static_cast<T>(xbits.get_val() - 1.0) *
338+
(std::is_same<T, double>::value ? static_cast<T>(ONE_OVER_F[p1])
339+
: ONE_OVER_F_FLOAT[p1]);
327340

328-
float p = fputil::polyeval(dx2, dx, c1, c2, c3);
341+
T dx2 = dx * dx;
329342

330-
float result = fputil::multiply_add(ex, 0x1.62e42ep-1f, LOG_F_FLOAT[p1] + p);
331-
return result;
332-
}
333-
334-
// x should be positive, normal finite value
335-
LIBC_INLINE static double log_eval(double x) {
336-
// For x = 2^ex * (1 + mx)
337-
// log(x) = ex * log(2) + log(1 + mx)
338-
using FPB = fputil::FPBits<double>;
339-
FPB bs(x);
340-
341-
double ex = static_cast<double>(bs.get_exponent());
342-
343-
// p1 is the leading 7 bits of mx, i.e.
344-
// p1 * 2^(-7) <= m_x < (p1 + 1) * 2^(-7).
345-
int p1 = static_cast<int>(bs.get_mantissa() >> (FPB::FRACTION_LEN - 7));
343+
const T *coeffs = nullptr;
344+
T log2_val;
345+
if constexpr (std::is_same<T, double>::value) {
346+
coeffs = LOG_COEFFS_DOUBLE;
347+
log2_val = LOG2_DOUBLE;
348+
} else {
349+
coeffs = LOG_COEFFS_FLOAT;
350+
log2_val = LOG2_FLOAT;
351+
}
346352

347-
// Set bs to (1 + (mx - p1*2^(-7))
348-
bs.set_uintval(bs.uintval() & (FPB::FRACTION_MASK >> 7));
349-
bs.set_biased_exponent(FPB::EXP_BIAS);
350-
// dx = (mx - p1*2^(-7)) / (1 + p1*2^(-7)).
351-
double dx = (bs.get_val() - 1.0) * ONE_OVER_F[p1];
353+
T c1 = fputil::multiply_add(dx, coeffs[1], coeffs[0]);
354+
T c2 = fputil::multiply_add(dx, coeffs[3], coeffs[2]);
355+
T c3 = fputil::multiply_add(dx, coeffs[5], coeffs[4]);
352356

353-
// Minimax polynomial of log(1 + dx) generated by Sollya with:
354-
// > P = fpminimax(log(1 + x)/x, 6, [|D...|], [0, 2^-7]);
355-
const double COEFFS[6] = {-0x1.ffffffffffffcp-2, 0x1.5555555552ddep-2,
356-
-0x1.ffffffefe562dp-3, 0x1.9999817d3a50fp-3,
357-
-0x1.554317b3f67a5p-3, 0x1.1dc5c45e09c18p-3};
358-
double dx2 = dx * dx;
359-
double c1 = fputil::multiply_add(dx, COEFFS[1], COEFFS[0]);
360-
double c2 = fputil::multiply_add(dx, COEFFS[3], COEFFS[2]);
361-
double c3 = fputil::multiply_add(dx, COEFFS[5], COEFFS[4]);
357+
T p = fputil::polyeval(dx2, dx, c1, c2, c3);
362358

363-
double p = fputil::polyeval(dx2, dx, c1, c2, c3);
364-
double result =
365-
fputil::multiply_add(ex, /*log(2)*/ 0x1.62e42fefa39efp-1, LOG_F[p1] + p);
366-
return result;
359+
if constexpr (std::is_same<T, double>::value)
360+
return fputil::multiply_add(ex, log2_val, LOG_F[p1] + p);
361+
else
362+
return fputil::multiply_add(ex, log2_val, LOG_F_FLOAT[p1] + p);
367363
}
368364

369365
// Rounding tests for 2^hi * (mid + lo) when the output might be denormal. We

libc/test/src/math/smoke/atanhf16_test.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ using LlvmLibcAtanhf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
1616

1717
TEST_F(LlvmLibcAtanhf16Test, SpecialNumbers) {
1818
LIBC_NAMESPACE::libc_errno = 0;
19+
EXPECT_FP_EQ_WITH_EXCEPTION_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::atanhf16(sNaN),
20+
FE_INVALID);
21+
EXPECT_MATH_ERRNO(0);
22+
1923
EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::atanhf16(aNaN));
2024
EXPECT_MATH_ERRNO(0);
2125

0 commit comments

Comments
 (0)