Skip to content

Commit

Permalink
[libc][math] Add option to set a specific exponent for frexp with Inf…
Browse files Browse the repository at this point in the history
…/NaN inputs. (#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
  • Loading branch information
lntue authored Oct 18, 2024
1 parent af90e7c commit b0dbd2c
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 4 deletions.
11 changes: 8 additions & 3 deletions libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ function(_get_compile_options_from_config output_var)
list(APPEND config_options "-DLIBC_ADD_NULL_CHECKS")
endif()

if(NOT "${LIBC_CONF_FREXP_INF_NAN_EXPONENT}" STREQUAL "")
list(APPEND config_options "-DLIBC_FREXP_INF_NAN_EXPONENT=${LIBC_CONF_FREXP_INF_NAN_EXPONENT}")
endif()

if(LIBC_CONF_MATH_OPTIMIZATIONS)
list(APPEND compile_options "-DLIBC_MATH=${LIBC_CONF_MATH_OPTIMIZATIONS}")
endif()

set(${output_var} ${config_options} PARENT_SCOPE)
endfunction(_get_compile_options_from_config)

Expand Down Expand Up @@ -170,9 +178,6 @@ function(_get_common_compile_options output_var flags)
list(APPEND compile_options "-Wthread-safety")
list(APPEND compile_options "-Wglobal-constructors")
endif()
if(LIBC_CONF_MATH_OPTIMIZATIONS)
list(APPEND compile_options "-DLIBC_MATH=${LIBC_CONF_MATH_OPTIMIZATIONS}")
endif()
elseif(MSVC)
list(APPEND compile_options "/EHs-c-")
list(APPEND compile_options "/GR-")
Expand Down
4 changes: 4 additions & 0 deletions libc/config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@
"LIBC_CONF_MATH_OPTIMIZATIONS": {
"value": 0,
"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."
},
"LIBC_CONF_FREXP_INF_NAN_EXPONENT": {
"value": "",
"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."
}
},
"qsort": {
Expand Down
1 change: 1 addition & 0 deletions libc/docs/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ to learn about the defaults for your platform and target.
* **"general" options**
- ``LIBC_ADD_NULL_CHECKS``: Add nullptr checks in the library's implementations to some functions for which passing nullptr is undefined behavior.
* **"math" options**
- ``LIBC_CONF_FREXP_INF_NAN_EXPONENT``: Set the specific exp value for Inf/NaN inputs.
- ``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.
* **"printf" options**
- ``LIBC_CONF_PRINTF_DISABLE_FIXED_POINT``: Disable printing fixed point values in printf and friends.
Expand Down
10 changes: 9 additions & 1 deletion libc/src/__support/FPUtil/ManipulationFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,16 @@ namespace fputil {
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE T frexp(T x, int &exp) {
FPBits<T> bits(x);
if (bits.is_inf_or_nan())
if (bits.is_inf_or_nan()) {
#ifdef LIBC_FREXP_INF_NAN_EXPONENT
// The value written back to the second parameter when calling
// frexp/frexpf/frexpl` with `+/-Inf`/`NaN` is unspecified in the standard.
// Set the exp value for Inf/NaN inputs explicitly to
// LIBC_FREXP_INF_NAN_EXPONENT if it is defined.
exp = LIBC_FREXP_INF_NAN_EXPONENT;
#endif // LIBC_FREXP_INF_NAN_EXPONENT
return x;
}
if (bits.is_zero()) {
exp = 0;
return x;
Expand Down
11 changes: 11 additions & 0 deletions libc/test/src/math/smoke/FrexpTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,19 @@ class FrexpTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
void testSpecialNumbers(FrexpFunc func) {
int exponent;
EXPECT_FP_EQ_ALL_ROUNDING(aNaN, func(aNaN, &exponent));
#ifdef LIBC_FREXP_INF_NAN_EXPONENT
EXPECT_EQ(LIBC_FREXP_INF_NAN_EXPONENT, exponent);
#endif // LIBC_FREXP_INF_NAN_EXPONENT

EXPECT_FP_EQ_ALL_ROUNDING(inf, func(inf, &exponent));
#ifdef LIBC_FREXP_INF_NAN_EXPONENT
EXPECT_EQ(LIBC_FREXP_INF_NAN_EXPONENT, exponent);
#endif // LIBC_FREXP_INF_NAN_EXPONENT

EXPECT_FP_EQ_ALL_ROUNDING(neg_inf, func(neg_inf, &exponent));
#ifdef LIBC_FREXP_INF_NAN_EXPONENT
EXPECT_EQ(LIBC_FREXP_INF_NAN_EXPONENT, exponent);
#endif // LIBC_FREXP_INF_NAN_EXPONENT

EXPECT_FP_EQ_ALL_ROUNDING(zero, func(zero, &exponent));
EXPECT_EQ(exponent, 0);
Expand Down

0 comments on commit b0dbd2c

Please sign in to comment.