[libc][math] Add option to set a specific exponent for frexp with Inf/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
This commit is contained in:
lntue 2024-10-18 09:58:15 -04:00 committed by GitHub
parent af90e7c516
commit b0dbd2ca5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 33 additions and 4 deletions

View File

@ -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)
@ -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-")

View File

@ -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": {

View File

@ -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.

View File

@ -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;

View File

@ -21,8 +21,19 @@ public:
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);