Skip to content

[libc][math] Fix signaling NaN handling for math functions. #133347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions libc/src/math/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4129,7 +4129,9 @@ add_entrypoint_object(
atan2f_float.h
DEPENDS
.inv_trigf_utils
libc.hdr.fenv_macros
libc.src.__support.FPUtil.double_double
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.nearest_integer
Expand All @@ -4147,6 +4149,7 @@ add_entrypoint_object(
DEPENDS
.atan_utils
libc.src.__support.FPUtil.double_double
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.nearest_integer
Expand Down
7 changes: 7 additions & 0 deletions libc/src/math/generic/acosf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,17 @@ LLVM_LIBC_FUNCTION(float, acosf, (float x)) {
0x1.921fb6p+1f)
: /* x == 1.0f */ 0.0f;

if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

// |x| <= +/-inf
if (x_abs <= 0x7f80'0000U) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
}

return x + FPBits::quiet_nan().get_val();
}

Expand Down
6 changes: 6 additions & 0 deletions libc/src/math/generic/asinf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,16 @@ LLVM_LIBC_FUNCTION(float, asinf, (float x)) {

// |x| > 1, return NaNs.
if (LIBC_UNLIKELY(x_abs > 0x3f80'0000U)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

if (x_abs <= 0x7f80'0000U) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
}

return FPBits::quiet_nan().get_val();
}

Expand Down
8 changes: 7 additions & 1 deletion libc/src/math/generic/asinhf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,14 @@ LLVM_LIBC_FUNCTION(float, asinhf, (float x)) {
};

if (LIBC_UNLIKELY(x_abs >= 0x4bdd'65a5U)) {
if (LIBC_UNLIKELY(xbits.is_inf_or_nan()))
if (LIBC_UNLIKELY(xbits.is_inf_or_nan())) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits_t::quiet_nan().get_val();
}

return x;
}

// Exceptional cases when x > 2^24.
switch (x_abs) {
Expand Down
6 changes: 5 additions & 1 deletion libc/src/math/generic/atan2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "src/math/atan2.h"
#include "atan_utils.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/double_double.h"
#include "src/__support/FPUtil/multiply_add.h"
Expand Down Expand Up @@ -111,8 +112,11 @@ LLVM_LIBC_FUNCTION(double, atan2, (double y, double x)) {
// Check for exceptional cases, whether inputs are 0, inf, nan, or close to
// overflow, or close to underflow.
if (LIBC_UNLIKELY(max_exp > 0x7ffU - 128U || min_exp < 128U)) {
if (x_bits.is_nan() || y_bits.is_nan())
if (x_bits.is_nan() || y_bits.is_nan()) {
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
unsigned x_except = x == 0.0 ? 0 : (FPBits(x_abs).is_inf() ? 2 : 1);
unsigned y_except = y == 0.0 ? 0 : (FPBits(y_abs).is_inf() ? 2 : 1);

Expand Down
7 changes: 6 additions & 1 deletion libc/src/math/generic/atan2f.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
//===----------------------------------------------------------------------===//

#include "src/math/atan2f.h"
#include "hdr/fenv_macros.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are other .cpp files edited by this PR that use FE_INVALID but don't include hdr/fenv_macros.h. Maybe we should leave this for another PR though. Thoughts @lntue?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

#include "inv_trigf_utils.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/double_double.h"
Expand Down Expand Up @@ -264,8 +266,11 @@ LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) {
double den_d = static_cast<double>(den_f);

if (LIBC_UNLIKELY(max_abs >= 0x7f80'0000U || num_d == 0.0)) {
if (x_bits.is_nan() || y_bits.is_nan())
if (x_bits.is_nan() || y_bits.is_nan()) {
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
double x_d = static_cast<double>(x);
double y_d = static_cast<double>(y);
size_t x_except = (x_d == 0.0) ? 0 : (x_abs == 0x7f80'0000 ? 2 : 1);
Expand Down
4 changes: 4 additions & 0 deletions libc/src/math/generic/atanhf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ LLVM_LIBC_FUNCTION(float, atanhf, (float x)) {
// |x| >= 1.0
if (LIBC_UNLIKELY(x_abs >= 0x3F80'0000U)) {
if (xbits.is_nan()) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
return x;
}
// |x| == 1.0
Expand Down
6 changes: 5 additions & 1 deletion libc/src/math/generic/cos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ LLVM_LIBC_FUNCTION(double, cos, (double x)) {
} else {
// Inf or NaN
if (LIBC_UNLIKELY(x_e > 2 * FPBits::EXP_BIAS)) {
// sin(+-Inf) = NaN
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
// cos(+-Inf) = NaN
if (xbits.get_mantissa() == 0) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
Expand Down
5 changes: 5 additions & 0 deletions libc/src/math/generic/cosf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ LLVM_LIBC_FUNCTION(float, cosf, (float x)) {

// x is inf or nan.
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

if (x_abs == 0x7f80'0000U) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
Expand Down
5 changes: 5 additions & 0 deletions libc/src/math/generic/cosf16.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ LLVM_LIBC_FUNCTION(float16, cosf16, (float16 x)) {

// cos(+/-inf) = NaN, and cos(NaN) = NaN
if (xbits.is_inf_or_nan()) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

if (xbits.is_inf()) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
Expand Down
5 changes: 5 additions & 0 deletions libc/src/math/generic/cospif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ LLVM_LIBC_FUNCTION(float, cospif, (float x)) {

// x is inf or nan.
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

if (x_abs == 0x7f80'0000U) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
Expand Down
4 changes: 4 additions & 0 deletions libc/src/math/generic/cospif16.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ LLVM_LIBC_FUNCTION(float16, cospif16, (float16 x)) {

// Check for NaN or infintiy values
if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
// If value is equal to infinity
if (x_abs == 0x7c00) {
fputil::set_errno_if_required(EDOM);
Expand Down
4 changes: 4 additions & 0 deletions libc/src/math/generic/erff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ LLVM_LIBC_FUNCTION(float, erff, (float x)) {
int sign = xbits.is_neg() ? 1 : 0;

if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
return (x_abs > 0x7f80'0000) ? x : ONE[sign];
}

Expand Down
7 changes: 6 additions & 1 deletion libc/src/math/generic/log1p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,12 @@ LLVM_LIBC_FUNCTION(double, log1p, (double x)) {
return FPBits_t::quiet_nan().get_val();
}
// x is +Inf or NaN
return x;
if (xbits.is_inf() && xbits.is_pos())
return x;

if (xbits.is_signaling_nan())
fputil::raise_except_if_required(FE_INVALID);
return FPBits_t::quiet_nan().get_val();
}
x_dd.hi = x;
} else {
Expand Down
5 changes: 5 additions & 0 deletions libc/src/math/generic/logf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ LLVM_LIBC_FUNCTION(float, logf, (float x)) {
return FPBits::quiet_nan().get_val();
}
// x is +inf or nan
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

return x;
}
}
Expand Down
5 changes: 5 additions & 0 deletions libc/src/math/generic/pow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@ LLVM_LIBC_FUNCTION(double, pow, (double x, double y)) {
uint64_t sign = 0;

///////// BEGIN - Check exceptional cases ////////////////////////////////////
// If x or y is signaling NaN
if (x_abs.is_signaling_nan() || y_abs.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

// The double precision number that is closest to 1 is (1 - 2^-53), which has
// log2(1 - 2^-53) ~ -1.715...p-53.
Expand Down
14 changes: 13 additions & 1 deletion libc/src/math/generic/powf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,12 @@ LLVM_LIBC_FUNCTION(float, powf, (float x, float y)) {
// |y * log2(x)| = 0 or > 151.
// Hence x^y will either overflow or underflow if x is not zero.
if (LIBC_UNLIKELY((y_abs & 0x0007'ffff) == 0) || (y_abs > 0x4f170000)) {
// y is signaling NaN
if (xbits.is_signaling_nan() || ybits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FloatBits::quiet_nan().get_val();
}

// Exceptional exponents.
if (y == 0.0f)
return 1.0f;
Expand Down Expand Up @@ -736,8 +742,8 @@ LLVM_LIBC_FUNCTION(float, powf, (float x, float y)) {
}
}
if (y_abs > 0x4f17'0000) {
// if y is NaN
if (y_abs > 0x7f80'0000) {
// y is NaN
if (x_u == 0x3f80'0000) { // x = 1.0f
// pow(1, NaN) = 1
return 1.0f;
Expand All @@ -759,6 +765,12 @@ LLVM_LIBC_FUNCTION(float, powf, (float x, float y)) {
// y is finite and non-zero.
if (LIBC_UNLIKELY(((x_u & 0x801f'ffffU) == 0) || x_u >= 0x7f80'0000U ||
x_u < 0x0080'0000U)) {
// if x is signaling NaN
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FloatBits::quiet_nan().get_val();
}

switch (x_u) {
case 0x3f80'0000: // x = 1.0f
return 1.0f;
Expand Down
5 changes: 5 additions & 0 deletions libc/src/math/generic/sin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ LLVM_LIBC_FUNCTION(double, sin, (double x)) {
// Inf or NaN
if (LIBC_UNLIKELY(x_e > 2 * FPBits::EXP_BIAS)) {
// sin(+-Inf) = NaN
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

if (xbits.get_mantissa() == 0) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
Expand Down
6 changes: 6 additions & 0 deletions libc/src/math/generic/sincos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ LLVM_LIBC_FUNCTION(void, sincos, (double x, double *sin_x, double *cos_x)) {
} else {
// Inf or NaN
if (LIBC_UNLIKELY(x_e > 2 * FPBits::EXP_BIAS)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
*sin_x = *cos_x = FPBits::quiet_nan().get_val();
return;
}

// sin(+-Inf) = NaN
if (xbits.get_mantissa() == 0) {
fputil::set_errno_if_required(EDOM);
Expand Down
6 changes: 6 additions & 0 deletions libc/src/math/generic/sincosf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ LLVM_LIBC_FUNCTION(void, sincosf, (float x, float *sinp, float *cosp)) {

// x is inf or nan.
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
*sinp = *cosp = FPBits::quiet_nan().get_val();
return;
}

if (x_abs == 0x7f80'0000U) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
Expand Down
5 changes: 5 additions & 0 deletions libc/src/math/generic/sinf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ LLVM_LIBC_FUNCTION(float, sinf, (float x)) {
#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS

if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

if (x_abs == 0x7f80'0000U) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
Expand Down
5 changes: 5 additions & 0 deletions libc/src/math/generic/sinf16.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ LLVM_LIBC_FUNCTION(float16, sinf16, (float16 x)) {
}

if (xbits.is_inf_or_nan()) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

if (xbits.is_inf()) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
Expand Down
5 changes: 5 additions & 0 deletions libc/src/math/generic/sinpif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {

// check for NaN values
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

if (x_abs == 0x7f80'0000U) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
Expand Down
4 changes: 4 additions & 0 deletions libc/src/math/generic/sinpif16.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ LLVM_LIBC_FUNCTION(float16, sinpif16, (float16 x)) {
if (LIBC_UNLIKELY(x_abs >= 0x6400)) {
// Check for NaN or infinity values
if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
// If value is equal to infinity
if (x_abs == 0x7c00) {
fputil::set_errno_if_required(EDOM);
Expand Down
4 changes: 4 additions & 0 deletions libc/src/math/generic/tan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ LLVM_LIBC_FUNCTION(double, tan, (double x)) {
} else {
// Inf or NaN
if (LIBC_UNLIKELY(x_e > 2 * FPBits::EXP_BIAS)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
// tan(+-Inf) = NaN
if (xbits.get_mantissa() == 0) {
fputil::set_errno_if_required(EDOM);
Expand Down
5 changes: 5 additions & 0 deletions libc/src/math/generic/tanf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ LLVM_LIBC_FUNCTION(float, tanf, (float x)) {
if (LIBC_UNLIKELY(x_abs > 0x4d56'd354U)) {
// Inf or NaN
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}

if (x_abs == 0x7f80'0000U) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
Expand Down
4 changes: 4 additions & 0 deletions libc/src/math/generic/tanf16.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ LLVM_LIBC_FUNCTION(float16, tanf16, (float16 x)) {

// tan(+/-inf) = NaN, and tan(NaN) = NaN
if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
// x = +/-inf
if (x_abs == 0x7c00) {
fputil::set_errno_if_required(EDOM);
Expand Down
5 changes: 5 additions & 0 deletions libc/src/math/generic/tanpif16.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ LLVM_LIBC_FUNCTION(float16, tanpif16, (float16 x)) {
if (LIBC_UNLIKELY(x_abs >= 0x6400)) {
// Check for NaN or infinity values
if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
// is inf
if (x_abs == 0x7c00) {
fputil::set_errno_if_required(EDOM);
fputil::raise_except_if_required(FE_INVALID);
Expand Down
Loading
Loading