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
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
@@ -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
@@ -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
7 changes: 7 additions & 0 deletions libc/src/math/generic/acosf.cpp
Original file line number Diff line number Diff line change
@@ -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();
}

6 changes: 6 additions & 0 deletions libc/src/math/generic/asinf.cpp
Original file line number Diff line number Diff line change
@@ -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();
}

8 changes: 7 additions & 1 deletion libc/src/math/generic/asinhf.cpp
Original file line number Diff line number Diff line change
@@ -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) {
6 changes: 5 additions & 1 deletion libc/src/math/generic/atan2.cpp
Original file line number Diff line number Diff line change
@@ -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"
@@ -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);

7 changes: 6 additions & 1 deletion libc/src/math/generic/atan2f.cpp
Original file line number Diff line number Diff line change
@@ -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"
@@ -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);
4 changes: 4 additions & 0 deletions libc/src/math/generic/atanhf.cpp
Original file line number Diff line number Diff line change
@@ -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
6 changes: 5 additions & 1 deletion libc/src/math/generic/cos.cpp
Original file line number Diff line number Diff line change
@@ -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);
5 changes: 5 additions & 0 deletions libc/src/math/generic/cosf.cpp
Original file line number Diff line number Diff line change
@@ -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);
5 changes: 5 additions & 0 deletions libc/src/math/generic/cosf16.cpp
Original file line number Diff line number Diff line change
@@ -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);
5 changes: 5 additions & 0 deletions libc/src/math/generic/cospif.cpp
Original file line number Diff line number Diff line change
@@ -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);
4 changes: 4 additions & 0 deletions libc/src/math/generic/cospif16.cpp
Original file line number Diff line number Diff line change
@@ -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);
4 changes: 4 additions & 0 deletions libc/src/math/generic/erff.cpp
Original file line number Diff line number Diff line change
@@ -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];
}

7 changes: 6 additions & 1 deletion libc/src/math/generic/log1p.cpp
Original file line number Diff line number Diff line change
@@ -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 {
5 changes: 5 additions & 0 deletions libc/src/math/generic/logf.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
}
5 changes: 5 additions & 0 deletions libc/src/math/generic/pow.cpp
Original file line number Diff line number Diff line change
@@ -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.
14 changes: 13 additions & 1 deletion libc/src/math/generic/powf.cpp
Original file line number Diff line number Diff line change
@@ -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;
@@ -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;
@@ -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;
5 changes: 5 additions & 0 deletions libc/src/math/generic/sin.cpp
Original file line number Diff line number Diff line change
@@ -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);
6 changes: 6 additions & 0 deletions libc/src/math/generic/sincos.cpp
Original file line number Diff line number Diff line change
@@ -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);
6 changes: 6 additions & 0 deletions libc/src/math/generic/sincosf.cpp
Original file line number Diff line number Diff line change
@@ -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);
5 changes: 5 additions & 0 deletions libc/src/math/generic/sinf.cpp
Original file line number Diff line number Diff line change
@@ -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);
5 changes: 5 additions & 0 deletions libc/src/math/generic/sinf16.cpp
Original file line number Diff line number Diff line change
@@ -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);
5 changes: 5 additions & 0 deletions libc/src/math/generic/sinpif.cpp
Original file line number Diff line number Diff line change
@@ -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);
4 changes: 4 additions & 0 deletions libc/src/math/generic/sinpif16.cpp
Original file line number Diff line number Diff line change
@@ -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);
4 changes: 4 additions & 0 deletions libc/src/math/generic/tan.cpp
Original file line number Diff line number Diff line change
@@ -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);
5 changes: 5 additions & 0 deletions libc/src/math/generic/tanf.cpp
Original file line number Diff line number Diff line change
@@ -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);
4 changes: 4 additions & 0 deletions libc/src/math/generic/tanf16.cpp
Original file line number Diff line number Diff line change
@@ -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);
5 changes: 5 additions & 0 deletions libc/src/math/generic/tanpif16.cpp
Original file line number Diff line number Diff line change
@@ -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);
Loading