Skip to content

Commit dfcb548

Browse files
committed
fixup! [libc][math][c23] Add exp2m1f C23 math function
1 parent 79c6351 commit dfcb548

File tree

6 files changed

+164
-33
lines changed

6 files changed

+164
-33
lines changed

libc/docs/math/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ Higher Math Functions
270270
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
271271
| exp2 | |check| | |check| | | | | 7.12.6.4 | F.10.3.4 |
272272
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
273-
| exp2m1 | | | | | | 7.12.6.5 | F.10.3.5 |
273+
| exp2m1 | |check| | | | | | 7.12.6.5 | F.10.3.5 |
274274
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
275275
| expm1 | |check| | |check| | | | | 7.12.6.6 | F.10.3.6 |
276276
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+

libc/src/math/generic/exp2m1f.cpp

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "src/__support/FPUtil/FEnvImpl.h"
1111
#include "src/__support/FPUtil/FPBits.h"
1212
#include "src/__support/FPUtil/PolyEval.h"
13+
#include "src/__support/FPUtil/except_value_utils.h"
1314
#include "src/__support/FPUtil/multiply_add.h"
1415
#include "src/__support/FPUtil/rounding_mode.h"
1516
#include "src/__support/common.h"
@@ -21,6 +22,40 @@
2122

2223
namespace LIBC_NAMESPACE {
2324

25+
static constexpr size_t N_EXCEPTS_LO = 8;
26+
27+
static constexpr fputil::ExceptValues<float, N_EXCEPTS_LO> EXP2M1F_EXCEPTS_LO =
28+
{{
29+
// (input, RZ output, RU offset, RD offset, RN offset)
30+
// x = 0x1.36dc8ep-36, exp2m1f(x) = 0x1.aef212p-37 (RZ)
31+
{0x2d9b'6e47U, 0x2d57'7909U, 1U, 0U, 0U},
32+
// x = 0x1.224936p-19, exp2m1f(x) = 0x1.926c0ep-20 (RZ)
33+
{0x3611'249bU, 0x35c9'3607U, 1U, 0U, 1U},
34+
// x = 0x1.d16d2p-20, exp2m1f(x) = 0x1.429becp-20 (RZ)
35+
{0x35e8'b690U, 0x35a1'4df6U, 1U, 0U, 1U},
36+
// x = 0x1.17949ep-14, exp2m1f(x) = 0x1.8397p-15 (RZ)
37+
{0x388b'ca4fU, 0x3841'cb80U, 1U, 0U, 1U},
38+
// x = -0x1.9c3e1ep-38, exp2m1f(x) = -0x1.1dbeacp-38 (RZ)
39+
{0xacce'1f0fU, 0xac8e'df56U, 0U, 1U, 0U},
40+
// x = -0x1.4d89b4p-32, exp2m1f(x) = -0x1.ce61b6p-33 (RZ)
41+
{0xafa6'c4daU, 0xaf67'30dbU, 0U, 1U, 1U},
42+
// x = -0x1.a6eac4p-10, exp2m1f(x) = -0x1.24fadap-10 (RZ)
43+
{0xbad3'7562U, 0xba92'7d6dU, 0U, 1U, 1U},
44+
// x = -0x1.e7526ep-6, exp2m1f(x) = -0x1.4e53dep-6 (RZ)
45+
{0xbcf3'a937U, 0xbca7'29efU, 0U, 1U, 1U},
46+
}};
47+
48+
static constexpr size_t N_EXCEPTS_HI = 2;
49+
50+
static constexpr fputil::ExceptValues<float, N_EXCEPTS_HI> EXP2M1F_EXCEPTS_HI =
51+
{{
52+
// (input, RZ output, RU offset, RD offset, RN offset)
53+
// x = 0x1.16a972p-1, exp2m1f(x) = 0x1.d545b2p-2 (RZ)
54+
{0x3f0b'54b9U, 0x3eea'a2d9U, 1U, 0U, 0U},
55+
// x = -0x1.9f12acp-5, exp2m1f(x) = -0x1.1ab68cp-5 (RZ)
56+
{0xbd4f'8956U, 0xbd0d'5b46U, 0U, 1U, 0U},
57+
}};
58+
2459
LLVM_LIBC_FUNCTION(float, exp2m1f, (float x)) {
2560
using FPBits = fputil::FPBits<float>;
2661
FPBits xbits(x);
@@ -32,7 +67,8 @@ LLVM_LIBC_FUNCTION(float, exp2m1f, (float x)) {
3267
if (LIBC_UNLIKELY(x_abs >= 0x4300'0000U || x_abs <= 0x3d00'0000U)) {
3368
// |x| <= 2^-5
3469
if (x_abs <= 0x3d00'0000) {
35-
// TODO: Handle exceptional values.
70+
if (auto r = EXP2M1F_EXCEPTS_LO.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
71+
return r.value();
3672

3773
// Minimax polynomial generated by Sollya with:
3874
// > display = hexadecimal;
@@ -63,26 +99,28 @@ LLVM_LIBC_FUNCTION(float, exp2m1f, (float x)) {
6399
// x >= 128 and 2^x - 1 rounds to +inf, or x is +inf or nan
64100
return x + FPBits::inf().get_val();
65101
}
102+
}
66103

67-
// x <= -25
68-
if (x <= -25.0f) {
69-
// 2^(-inf) - 1 = -1
70-
if (xbits.is_inf())
71-
return -1.0f;
72-
// 2^nan - 1 = nan
73-
if (xbits.is_nan())
74-
return x;
75-
76-
int rounding = fputil::quick_get_round();
77-
if (rounding == FE_UPWARD || rounding == FE_TOWARDZERO)
78-
return -0x1.ffff'fep-1f; // -1.0f + 0x1.0p-24f
79-
80-
fputil::set_errno_if_required(ERANGE);
81-
fputil::raise_except_if_required(FE_UNDERFLOW);
104+
if (LIBC_UNLIKELY(x <= -25.0f)) {
105+
// 2^(-inf) - 1 = -1
106+
if (xbits.is_inf())
82107
return -1.0f;
83-
}
108+
// 2^nan - 1 = nan
109+
if (xbits.is_nan())
110+
return x;
111+
112+
int rounding = fputil::quick_get_round();
113+
if (rounding == FE_UPWARD || rounding == FE_TOWARDZERO)
114+
return -0x1.ffff'fep-1f; // -1.0f + 0x1.0p-24f
115+
116+
fputil::set_errno_if_required(ERANGE);
117+
fputil::raise_except_if_required(FE_UNDERFLOW);
118+
return -1.0f;
84119
}
85120
121+
if (auto r = EXP2M1F_EXCEPTS_HI.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
122+
return r.value();
123+
86124
// For -25 < x < 128, to compute 2^x, we perform the following range
87125
// reduction: find hi, mid, lo such that:
88126
// x = hi + mid + lo, in which:

libc/test/src/math/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,22 @@ add_fp_unittest(
637637
libc.src.__support.FPUtil.fp_bits
638638
)
639639

640+
add_fp_unittest(
641+
exp2m1f_test
642+
NEED_MPFR
643+
SUITE
644+
libc-math-unittests
645+
SRCS
646+
exp2m1f_test.cpp
647+
DEPENDS
648+
libc.include.llvm-libc-macros.math_macros
649+
libc.include.llvm-libc-macros.stdint_macros
650+
libc.src.errno.errno
651+
libc.src.math.exp2m1f
652+
libc.src.__support.CPP.array
653+
libc.src.__support.FPUtil.fp_bits
654+
)
655+
640656
add_fp_unittest(
641657
exp10f_test
642658
NEED_MPFR

libc/test/src/math/exp2m1f_test.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===-- Unittests for exp2m1f ---------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "include/llvm-libc-macros/math-macros.h"
10+
#include "src/__support/CPP/array.h"
11+
#include "src/__support/FPUtil/FPBits.h"
12+
#include "src/errno/libc_errno.h"
13+
#include "src/math/exp2m1f.h"
14+
#include "test/UnitTest/FPMatcher.h"
15+
#include "test/UnitTest/Test.h"
16+
#include "utils/MPFRWrapper/MPFRUtils.h"
17+
18+
#include <stdint.h>
19+
20+
using LlvmLibcExp2m1fTest = LIBC_NAMESPACE::testing::FPTest<float>;
21+
22+
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
23+
24+
TEST_F(LlvmLibcExp2m1fTest, TrickyInputs) {
25+
constexpr LIBC_NAMESPACE::cpp::array<float, 10> INPUTS = {
26+
// EXP2M1F_EXCEPTS_LO
27+
0x1.36dc8ep-36,
28+
0x1.224936p-19,
29+
0x1.d16d2p-20,
30+
0x1.17949ep-14,
31+
-0x1.9c3e1ep-38,
32+
-0x1.4d89b4p-32,
33+
-0x1.a6eac4p-10,
34+
-0x1.e7526ep-6,
35+
// EXP2M1F_EXCEPTS_HI
36+
0x1.16a972p-1,
37+
-0x1.9f12acp-5,
38+
};
39+
40+
for (float x : INPUTS) {
41+
LIBC_NAMESPACE::libc_errno = 0;
42+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2m1, x,
43+
LIBC_NAMESPACE::exp2m1f(x), 0.5);
44+
EXPECT_MATH_ERRNO(0);
45+
}
46+
}
47+
48+
TEST_F(LlvmLibcExp2m1fTest, InFloatRange) {
49+
constexpr uint32_t COUNT = 100'000;
50+
constexpr uint32_t STEP = UINT32_MAX / COUNT;
51+
for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
52+
float x = FPBits(v).get_val();
53+
if (isnan(x) || isinf(x))
54+
continue;
55+
LIBC_NAMESPACE::libc_errno = 0;
56+
float result = LIBC_NAMESPACE::exp2m1f(x);
57+
58+
// If the computation resulted in an error or did not produce valid result
59+
// in the single-precision floating point range, then ignore comparing with
60+
// MPFR result as MPFR can still produce valid results because of its
61+
// wider precision.
62+
if (isnan(result) || isinf(result) || LIBC_NAMESPACE::libc_errno != 0)
63+
continue;
64+
ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp2m1, x,
65+
LIBC_NAMESPACE::exp2m1f(x), 0.5);
66+
}
67+
}

libc/test/src/math/smoke/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,6 @@ add_fp_unittest(
780780
SRCS
781781
exp2m1f_test.cpp
782782
DEPENDS
783-
libc.src.__support.FPUtil.fp_bits
784783
libc.src.errno.errno
785784
libc.src.math.exp2m1f
786785
)

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

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,26 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#include "src/__support/FPUtil/FPBits.h"
109
#include "src/errno/libc_errno.h"
1110
#include "src/math/exp2m1f.h"
1211
#include "test/UnitTest/FPMatcher.h"
1312
#include "test/UnitTest/Test.h"
1413

1514
using LlvmLibcExp2m1fTest = LIBC_NAMESPACE::testing::FPTest<float>;
15+
using LIBC_NAMESPACE::fputil::testing::ForceRoundingMode;
16+
using LIBC_NAMESPACE::fputil::testing::RoundingMode;
1617

1718
TEST_F(LlvmLibcExp2m1fTest, SpecialNumbers) {
1819
LIBC_NAMESPACE::libc_errno = 0;
1920

2021
EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::exp2m1f(aNaN));
2122
EXPECT_MATH_ERRNO(0);
22-
2323
EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::exp2m1f(inf));
2424
EXPECT_MATH_ERRNO(0);
25-
2625
EXPECT_FP_EQ_ALL_ROUNDING(-1.0f, LIBC_NAMESPACE::exp2m1f(neg_inf));
2726
EXPECT_MATH_ERRNO(0);
28-
2927
EXPECT_FP_EQ_ALL_ROUNDING(0.0f, LIBC_NAMESPACE::exp2m1f(0.0f));
3028
EXPECT_MATH_ERRNO(0);
31-
3229
EXPECT_FP_EQ_ALL_ROUNDING(-0.0f, LIBC_NAMESPACE::exp2m1f(-0.0f));
3330
EXPECT_MATH_ERRNO(0);
3431

@@ -40,18 +37,32 @@ TEST_F(LlvmLibcExp2m1fTest, SpecialNumbers) {
4037

4138
TEST_F(LlvmLibcExp2m1fTest, Overflow) {
4239
LIBC_NAMESPACE::libc_errno = 0;
43-
EXPECT_FP_EQ_WITH_EXCEPTION(
44-
inf, LIBC_NAMESPACE::exp2m1f(FPBits(0x7f7f'ffffU).get_val()),
45-
FE_OVERFLOW);
40+
41+
EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::exp2m1f(0x1.fffffep+127),
42+
FE_OVERFLOW);
43+
EXPECT_MATH_ERRNO(ERANGE);
44+
45+
EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::exp2m1f(128.0f),
46+
FE_OVERFLOW);
47+
EXPECT_MATH_ERRNO(ERANGE);
48+
49+
EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::exp2m1f(0x1.000002p+7),
50+
FE_OVERFLOW);
51+
EXPECT_MATH_ERRNO(ERANGE);
52+
}
53+
54+
TEST_F(LlvmLibcExp2m1fTest, Underflow) {
55+
LIBC_NAMESPACE::libc_errno = 0;
56+
57+
EXPECT_FP_EQ_WITH_EXCEPTION(-1.0f, LIBC_NAMESPACE::exp2m1f(-0x1.fffffep+127),
58+
FE_UNDERFLOW);
4659
EXPECT_MATH_ERRNO(ERANGE);
4760

48-
EXPECT_FP_EQ_WITH_EXCEPTION(
49-
inf, LIBC_NAMESPACE::exp2m1f(FPBits(0x4300'0000U).get_val()),
50-
FE_OVERFLOW);
61+
EXPECT_FP_EQ_WITH_EXCEPTION(-1.0f, LIBC_NAMESPACE::exp2m1f(-25.0f),
62+
FE_UNDERFLOW);
5163
EXPECT_MATH_ERRNO(ERANGE);
5264

53-
EXPECT_FP_EQ_WITH_EXCEPTION(
54-
inf, LIBC_NAMESPACE::exp2m1f(FPBits(0x4300'0001U).get_val()),
55-
FE_OVERFLOW);
65+
EXPECT_FP_EQ_WITH_EXCEPTION(-1.0f, LIBC_NAMESPACE::exp2m1f(-0x1.900002p4),
66+
FE_UNDERFLOW);
5667
EXPECT_MATH_ERRNO(ERANGE);
5768
}

0 commit comments

Comments
 (0)