[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:
parent
af90e7c516
commit
b0dbd2ca5b
@ -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-")
|
||||
|
@ -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": {
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user