Skip to content

Commit b0dbd2c

Browse files
authored
[libc][math] Add option to set a specific exponent for frexp with Inf/NaN inputs. (llvm#112387)
In IEEE 754 and C standards, when calling `frexp` with Inf/Nan inputs, the exponent result is unspecified. In this case, FreeBSD libc and musl just passthrough `exp`, while glibc, FreeBSD libm set exp = 0, and MSVC set exp = -1. By default, LLVM libc will passthrough `exp` just as FreeBSD libc and musl, but we also allow users to explicitly choose the return exp value in this case for compatibility with other libc. Notice that, gcc did generate passthrough `exp` for `frexp(NaN/Inf, exp)`: https://godbolt.org/z/sM8fEej4E
1 parent af90e7c commit b0dbd2c

File tree

5 files changed

+33
-4
lines changed

5 files changed

+33
-4
lines changed

libc/cmake/modules/LLVMLibCCompileOptionRules.cmake

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ function(_get_compile_options_from_config output_var)
7979
list(APPEND config_options "-DLIBC_ADD_NULL_CHECKS")
8080
endif()
8181

82+
if(NOT "${LIBC_CONF_FREXP_INF_NAN_EXPONENT}" STREQUAL "")
83+
list(APPEND config_options "-DLIBC_FREXP_INF_NAN_EXPONENT=${LIBC_CONF_FREXP_INF_NAN_EXPONENT}")
84+
endif()
85+
86+
if(LIBC_CONF_MATH_OPTIMIZATIONS)
87+
list(APPEND compile_options "-DLIBC_MATH=${LIBC_CONF_MATH_OPTIMIZATIONS}")
88+
endif()
89+
8290
set(${output_var} ${config_options} PARENT_SCOPE)
8391
endfunction(_get_compile_options_from_config)
8492

@@ -170,9 +178,6 @@ function(_get_common_compile_options output_var flags)
170178
list(APPEND compile_options "-Wthread-safety")
171179
list(APPEND compile_options "-Wglobal-constructors")
172180
endif()
173-
if(LIBC_CONF_MATH_OPTIMIZATIONS)
174-
list(APPEND compile_options "-DLIBC_MATH=${LIBC_CONF_MATH_OPTIMIZATIONS}")
175-
endif()
176181
elseif(MSVC)
177182
list(APPEND compile_options "/EHs-c-")
178183
list(APPEND compile_options "/GR-")

libc/config/config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@
8787
"LIBC_CONF_MATH_OPTIMIZATIONS": {
8888
"value": 0,
8989
"doc": "Configures optimizations for math functions. Values accepted are LIBC_MATH_SKIP_ACCURATE_PASS, LIBC_MATH_SMALL_TABLES, LIBC_MATH_NO_ERRNO, LIBC_MATH_NO_EXCEPT, and LIBC_MATH_FAST."
90+
},
91+
"LIBC_CONF_FREXP_INF_NAN_EXPONENT": {
92+
"value": "",
93+
"doc": "The value written back to the second parameter when calling frexp/frexpf/frexpl` with `+/-Inf`/`NaN` is unspecified. Configue an explicit exp value for Inf/NaN inputs."
9094
}
9195
},
9296
"qsort": {

libc/docs/configure.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ to learn about the defaults for your platform and target.
3333
* **"general" options**
3434
- ``LIBC_ADD_NULL_CHECKS``: Add nullptr checks in the library's implementations to some functions for which passing nullptr is undefined behavior.
3535
* **"math" options**
36+
- ``LIBC_CONF_FREXP_INF_NAN_EXPONENT``: Set the specific exp value for Inf/NaN inputs.
3637
- ``LIBC_CONF_MATH_OPTIMIZATIONS``: Configures optimizations for math functions. Values accepted are LIBC_MATH_SKIP_ACCURATE_PASS, LIBC_MATH_SMALL_TABLES, LIBC_MATH_NO_ERRNO, LIBC_MATH_NO_EXCEPT, and LIBC_MATH_FAST.
3738
* **"printf" options**
3839
- ``LIBC_CONF_PRINTF_DISABLE_FIXED_POINT``: Disable printing fixed point values in printf and friends.

libc/src/__support/FPUtil/ManipulationFunctions.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,16 @@ namespace fputil {
3131
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
3232
LIBC_INLINE T frexp(T x, int &exp) {
3333
FPBits<T> bits(x);
34-
if (bits.is_inf_or_nan())
34+
if (bits.is_inf_or_nan()) {
35+
#ifdef LIBC_FREXP_INF_NAN_EXPONENT
36+
// The value written back to the second parameter when calling
37+
// frexp/frexpf/frexpl` with `+/-Inf`/`NaN` is unspecified in the standard.
38+
// Set the exp value for Inf/NaN inputs explicitly to
39+
// LIBC_FREXP_INF_NAN_EXPONENT if it is defined.
40+
exp = LIBC_FREXP_INF_NAN_EXPONENT;
41+
#endif // LIBC_FREXP_INF_NAN_EXPONENT
3542
return x;
43+
}
3644
if (bits.is_zero()) {
3745
exp = 0;
3846
return x;

libc/test/src/math/smoke/FrexpTest.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,19 @@ class FrexpTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
2121
void testSpecialNumbers(FrexpFunc func) {
2222
int exponent;
2323
EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(aNaN, &exponent));
24+
#ifdef LIBC_FREXP_INF_NAN_EXPONENT
25+
EXPECT_EQ(LIBC_FREXP_INF_NAN_EXPONENT, exponent);
26+
#endif // LIBC_FREXP_INF_NAN_EXPONENT
27+
2428
EXPECT_FP_EQ_ALL_ROUNDING(inf, func(inf, &exponent));
29+
#ifdef LIBC_FREXP_INF_NAN_EXPONENT
30+
EXPECT_EQ(LIBC_FREXP_INF_NAN_EXPONENT, exponent);
31+
#endif // LIBC_FREXP_INF_NAN_EXPONENT
32+
2533
EXPECT_FP_EQ_ALL_ROUNDING(neg_inf, func(neg_inf, &exponent));
34+
#ifdef LIBC_FREXP_INF_NAN_EXPONENT
35+
EXPECT_EQ(LIBC_FREXP_INF_NAN_EXPONENT, exponent);
36+
#endif // LIBC_FREXP_INF_NAN_EXPONENT
2637

2738
EXPECT_FP_EQ_ALL_ROUNDING(zero, func(zero, &exponent));
2839
EXPECT_EQ(exponent, 0);

0 commit comments

Comments
 (0)