Skip to content

Commit fdf2094

Browse files
authored
[libc][math] Fix signaling NaN handling for math functions. (#133347)
Add tests for signaling NaNs, and fix function behavior for handling signaling NaN input. Fixes #124812
1 parent dae0ef5 commit fdf2094

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+399
-12
lines changed

libc/src/math/generic/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4169,7 +4169,9 @@ add_entrypoint_object(
41694169
atan2f_float.h
41704170
DEPENDS
41714171
.inv_trigf_utils
4172+
libc.hdr.fenv_macros
41724173
libc.src.__support.FPUtil.double_double
4174+
libc.src.__support.FPUtil.fenv_impl
41734175
libc.src.__support.FPUtil.fp_bits
41744176
libc.src.__support.FPUtil.multiply_add
41754177
libc.src.__support.FPUtil.nearest_integer
@@ -4187,6 +4189,7 @@ add_entrypoint_object(
41874189
DEPENDS
41884190
.atan_utils
41894191
libc.src.__support.FPUtil.double_double
4192+
libc.src.__support.FPUtil.fenv_impl
41904193
libc.src.__support.FPUtil.fp_bits
41914194
libc.src.__support.FPUtil.multiply_add
41924195
libc.src.__support.FPUtil.nearest_integer

libc/src/math/generic/acosf.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,17 @@ LLVM_LIBC_FUNCTION(float, acosf, (float x)) {
8484
0x1.921fb6p+1f)
8585
: /* x == 1.0f */ 0.0f;
8686

87+
if (xbits.is_signaling_nan()) {
88+
fputil::raise_except_if_required(FE_INVALID);
89+
return FPBits::quiet_nan().get_val();
90+
}
91+
92+
// |x| <= +/-inf
8793
if (x_abs <= 0x7f80'0000U) {
8894
fputil::set_errno_if_required(EDOM);
8995
fputil::raise_except_if_required(FE_INVALID);
9096
}
97+
9198
return x + FPBits::quiet_nan().get_val();
9299
}
93100

libc/src/math/generic/asinf.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,16 @@ LLVM_LIBC_FUNCTION(float, asinf, (float x)) {
108108

109109
// |x| > 1, return NaNs.
110110
if (LIBC_UNLIKELY(x_abs > 0x3f80'0000U)) {
111+
if (xbits.is_signaling_nan()) {
112+
fputil::raise_except_if_required(FE_INVALID);
113+
return FPBits::quiet_nan().get_val();
114+
}
115+
111116
if (x_abs <= 0x7f80'0000U) {
112117
fputil::set_errno_if_required(EDOM);
113118
fputil::raise_except_if_required(FE_INVALID);
114119
}
120+
115121
return FPBits::quiet_nan().get_val();
116122
}
117123

libc/src/math/generic/asinhf.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,14 @@ LLVM_LIBC_FUNCTION(float, asinhf, (float x)) {
6161
};
6262

6363
if (LIBC_UNLIKELY(x_abs >= 0x4bdd'65a5U)) {
64-
if (LIBC_UNLIKELY(xbits.is_inf_or_nan()))
64+
if (LIBC_UNLIKELY(xbits.is_inf_or_nan())) {
65+
if (xbits.is_signaling_nan()) {
66+
fputil::raise_except_if_required(FE_INVALID);
67+
return FPBits_t::quiet_nan().get_val();
68+
}
69+
6570
return x;
71+
}
6672

6773
// Exceptional cases when x > 2^24.
6874
switch (x_abs) {

libc/src/math/generic/atan2.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

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

libc/src/math/generic/atan2f.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "src/math/atan2f.h"
10+
#include "hdr/fenv_macros.h"
1011
#include "inv_trigf_utils.h"
12+
#include "src/__support/FPUtil/FEnvImpl.h"
1113
#include "src/__support/FPUtil/FPBits.h"
1214
#include "src/__support/FPUtil/PolyEval.h"
1315
#include "src/__support/FPUtil/double_double.h"
@@ -264,8 +266,11 @@ LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) {
264266
double den_d = static_cast<double>(den_f);
265267

266268
if (LIBC_UNLIKELY(max_abs >= 0x7f80'0000U || num_d == 0.0)) {
267-
if (x_bits.is_nan() || y_bits.is_nan())
269+
if (x_bits.is_nan() || y_bits.is_nan()) {
270+
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
271+
fputil::raise_except_if_required(FE_INVALID);
268272
return FPBits::quiet_nan().get_val();
273+
}
269274
double x_d = static_cast<double>(x);
270275
double y_d = static_cast<double>(y);
271276
size_t x_except = (x_d == 0.0) ? 0 : (x_abs == 0x7f80'0000 ? 2 : 1);

libc/src/math/generic/atanhf.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ LLVM_LIBC_FUNCTION(float, atanhf, (float x)) {
2424
// |x| >= 1.0
2525
if (LIBC_UNLIKELY(x_abs >= 0x3F80'0000U)) {
2626
if (xbits.is_nan()) {
27+
if (xbits.is_signaling_nan()) {
28+
fputil::raise_except_if_required(FE_INVALID);
29+
return FPBits::quiet_nan().get_val();
30+
}
2731
return x;
2832
}
2933
// |x| == 1.0

libc/src/math/generic/cos.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@ LLVM_LIBC_FUNCTION(double, cos, (double x)) {
6565
} else {
6666
// Inf or NaN
6767
if (LIBC_UNLIKELY(x_e > 2 * FPBits::EXP_BIAS)) {
68-
// sin(+-Inf) = NaN
68+
if (xbits.is_signaling_nan()) {
69+
fputil::raise_except_if_required(FE_INVALID);
70+
return FPBits::quiet_nan().get_val();
71+
}
72+
// cos(+-Inf) = NaN
6973
if (xbits.get_mantissa() == 0) {
7074
fputil::set_errno_if_required(EDOM);
7175
fputil::raise_except_if_required(FE_INVALID);

libc/src/math/generic/cosf.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ LLVM_LIBC_FUNCTION(float, cosf, (float x)) {
117117

118118
// x is inf or nan.
119119
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
120+
if (xbits.is_signaling_nan()) {
121+
fputil::raise_except_if_required(FE_INVALID);
122+
return FPBits::quiet_nan().get_val();
123+
}
124+
120125
if (x_abs == 0x7f80'0000U) {
121126
fputil::set_errno_if_required(EDOM);
122127
fputil::raise_except_if_required(FE_INVALID);

libc/src/math/generic/cosf16.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ LLVM_LIBC_FUNCTION(float16, cosf16, (float16 x)) {
6767

6868
// cos(+/-inf) = NaN, and cos(NaN) = NaN
6969
if (xbits.is_inf_or_nan()) {
70+
if (xbits.is_signaling_nan()) {
71+
fputil::raise_except_if_required(FE_INVALID);
72+
return FPBits::quiet_nan().get_val();
73+
}
74+
7075
if (xbits.is_inf()) {
7176
fputil::set_errno_if_required(EDOM);
7277
fputil::raise_except_if_required(FE_INVALID);

libc/src/math/generic/cospif.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ LLVM_LIBC_FUNCTION(float, cospif, (float x)) {
6666

6767
// x is inf or nan.
6868
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
69+
if (xbits.is_signaling_nan()) {
70+
fputil::raise_except_if_required(FE_INVALID);
71+
return FPBits::quiet_nan().get_val();
72+
}
73+
6974
if (x_abs == 0x7f80'0000U) {
7075
fputil::set_errno_if_required(EDOM);
7176
fputil::raise_except_if_required(FE_INVALID);

libc/src/math/generic/cospif16.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ LLVM_LIBC_FUNCTION(float16, cospif16, (float16 x)) {
5454

5555
// Check for NaN or infintiy values
5656
if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
57+
if (xbits.is_signaling_nan()) {
58+
fputil::raise_except_if_required(FE_INVALID);
59+
return FPBits::quiet_nan().get_val();
60+
}
5761
// If value is equal to infinity
5862
if (x_abs == 0x7c00) {
5963
fputil::set_errno_if_required(EDOM);

libc/src/math/generic/erff.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ LLVM_LIBC_FUNCTION(float, erff, (float x)) {
135135
int sign = xbits.is_neg() ? 1 : 0;
136136

137137
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
138+
if (xbits.is_signaling_nan()) {
139+
fputil::raise_except_if_required(FE_INVALID);
140+
return FPBits::quiet_nan().get_val();
141+
}
138142
return (x_abs > 0x7f80'0000) ? x : ONE[sign];
139143
}
140144

libc/src/math/generic/log1p.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,12 @@ LLVM_LIBC_FUNCTION(double, log1p, (double x)) {
910910
return FPBits_t::quiet_nan().get_val();
911911
}
912912
// x is +Inf or NaN
913-
return x;
913+
if (xbits.is_inf() && xbits.is_pos())
914+
return x;
915+
916+
if (xbits.is_signaling_nan())
917+
fputil::raise_except_if_required(FE_INVALID);
918+
return FPBits_t::quiet_nan().get_val();
914919
}
915920
x_dd.hi = x;
916921
} else {

libc/src/math/generic/logf.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ LLVM_LIBC_FUNCTION(float, logf, (float x)) {
132132
return FPBits::quiet_nan().get_val();
133133
}
134134
// x is +inf or nan
135+
if (xbits.is_signaling_nan()) {
136+
fputil::raise_except_if_required(FE_INVALID);
137+
return FPBits::quiet_nan().get_val();
138+
}
139+
135140
return x;
136141
}
137142
}

libc/src/math/generic/pow.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ LLVM_LIBC_FUNCTION(double, pow, (double x, double y)) {
217217
uint64_t sign = 0;
218218

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

221226
// The double precision number that is closest to 1 is (1 - 2^-53), which has
222227
// log2(1 - 2^-53) ~ -1.715...p-53.

libc/src/math/generic/powf.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,12 @@ LLVM_LIBC_FUNCTION(float, powf, (float x, float y)) {
664664
// |y * log2(x)| = 0 or > 151.
665665
// Hence x^y will either overflow or underflow if x is not zero.
666666
if (LIBC_UNLIKELY((y_abs & 0x0007'ffff) == 0) || (y_abs > 0x4f170000)) {
667+
// y is signaling NaN
668+
if (xbits.is_signaling_nan() || ybits.is_signaling_nan()) {
669+
fputil::raise_except_if_required(FE_INVALID);
670+
return FloatBits::quiet_nan().get_val();
671+
}
672+
667673
// Exceptional exponents.
668674
if (y == 0.0f)
669675
return 1.0f;
@@ -736,8 +742,8 @@ LLVM_LIBC_FUNCTION(float, powf, (float x, float y)) {
736742
}
737743
}
738744
if (y_abs > 0x4f17'0000) {
745+
// if y is NaN
739746
if (y_abs > 0x7f80'0000) {
740-
// y is NaN
741747
if (x_u == 0x3f80'0000) { // x = 1.0f
742748
// pow(1, NaN) = 1
743749
return 1.0f;
@@ -759,6 +765,12 @@ LLVM_LIBC_FUNCTION(float, powf, (float x, float y)) {
759765
// y is finite and non-zero.
760766
if (LIBC_UNLIKELY(((x_u & 0x801f'ffffU) == 0) || x_u >= 0x7f80'0000U ||
761767
x_u < 0x0080'0000U)) {
768+
// if x is signaling NaN
769+
if (xbits.is_signaling_nan()) {
770+
fputil::raise_except_if_required(FE_INVALID);
771+
return FloatBits::quiet_nan().get_val();
772+
}
773+
762774
switch (x_u) {
763775
case 0x3f80'0000: // x = 1.0f
764776
return 1.0f;

libc/src/math/generic/sin.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ LLVM_LIBC_FUNCTION(double, sin, (double x)) {
7777
// Inf or NaN
7878
if (LIBC_UNLIKELY(x_e > 2 * FPBits::EXP_BIAS)) {
7979
// sin(+-Inf) = NaN
80+
if (xbits.is_signaling_nan()) {
81+
fputil::raise_except_if_required(FE_INVALID);
82+
return FPBits::quiet_nan().get_val();
83+
}
84+
8085
if (xbits.get_mantissa() == 0) {
8186
fputil::set_errno_if_required(EDOM);
8287
fputil::raise_except_if_required(FE_INVALID);

libc/src/math/generic/sincos.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ LLVM_LIBC_FUNCTION(void, sincos, (double x, double *sin_x, double *cos_x)) {
8585
} else {
8686
// Inf or NaN
8787
if (LIBC_UNLIKELY(x_e > 2 * FPBits::EXP_BIAS)) {
88+
if (xbits.is_signaling_nan()) {
89+
fputil::raise_except_if_required(FE_INVALID);
90+
*sin_x = *cos_x = FPBits::quiet_nan().get_val();
91+
return;
92+
}
93+
8894
// sin(+-Inf) = NaN
8995
if (xbits.get_mantissa() == 0) {
9096
fputil::set_errno_if_required(EDOM);

libc/src/math/generic/sincosf.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ LLVM_LIBC_FUNCTION(void, sincosf, (float x, float *sinp, float *cosp)) {
145145

146146
// x is inf or nan.
147147
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
148+
if (xbits.is_signaling_nan()) {
149+
fputil::raise_except_if_required(FE_INVALID);
150+
*sinp = *cosp = FPBits::quiet_nan().get_val();
151+
return;
152+
}
153+
148154
if (x_abs == 0x7f80'0000U) {
149155
fputil::set_errno_if_required(EDOM);
150156
fputil::raise_except_if_required(FE_INVALID);

libc/src/math/generic/sinf.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ LLVM_LIBC_FUNCTION(float, sinf, (float x)) {
136136
#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
137137

138138
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
139+
if (xbits.is_signaling_nan()) {
140+
fputil::raise_except_if_required(FE_INVALID);
141+
return FPBits::quiet_nan().get_val();
142+
}
143+
139144
if (x_abs == 0x7f80'0000U) {
140145
fputil::set_errno_if_required(EDOM);
141146
fputil::raise_except_if_required(FE_INVALID);

libc/src/math/generic/sinf16.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ LLVM_LIBC_FUNCTION(float16, sinf16, (float16 x)) {
8787
}
8888

8989
if (xbits.is_inf_or_nan()) {
90+
if (xbits.is_signaling_nan()) {
91+
fputil::raise_except_if_required(FE_INVALID);
92+
return FPBits::quiet_nan().get_val();
93+
}
94+
9095
if (xbits.is_inf()) {
9196
fputil::set_errno_if_required(EDOM);
9297
fputil::raise_except_if_required(FE_INVALID);

libc/src/math/generic/sinpif.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
8383

8484
// check for NaN values
8585
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
86+
if (xbits.is_signaling_nan()) {
87+
fputil::raise_except_if_required(FE_INVALID);
88+
return FPBits::quiet_nan().get_val();
89+
}
90+
8691
if (x_abs == 0x7f80'0000U) {
8792
fputil::set_errno_if_required(EDOM);
8893
fputil::raise_except_if_required(FE_INVALID);

libc/src/math/generic/sinpif16.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ LLVM_LIBC_FUNCTION(float16, sinpif16, (float16 x)) {
5050
if (LIBC_UNLIKELY(x_abs >= 0x6400)) {
5151
// Check for NaN or infinity values
5252
if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
53+
if (xbits.is_signaling_nan()) {
54+
fputil::raise_except_if_required(FE_INVALID);
55+
return FPBits::quiet_nan().get_val();
56+
}
5357
// If value is equal to infinity
5458
if (x_abs == 0x7c00) {
5559
fputil::set_errno_if_required(EDOM);

libc/src/math/generic/tan.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ LLVM_LIBC_FUNCTION(double, tan, (double x)) {
163163
} else {
164164
// Inf or NaN
165165
if (LIBC_UNLIKELY(x_e > 2 * FPBits::EXP_BIAS)) {
166+
if (xbits.is_signaling_nan()) {
167+
fputil::raise_except_if_required(FE_INVALID);
168+
return FPBits::quiet_nan().get_val();
169+
}
166170
// tan(+-Inf) = NaN
167171
if (xbits.get_mantissa() == 0) {
168172
fputil::set_errno_if_required(EDOM);

libc/src/math/generic/tanf.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ LLVM_LIBC_FUNCTION(float, tanf, (float x)) {
113113
if (LIBC_UNLIKELY(x_abs > 0x4d56'd354U)) {
114114
// Inf or NaN
115115
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
116+
if (xbits.is_signaling_nan()) {
117+
fputil::raise_except_if_required(FE_INVALID);
118+
return FPBits::quiet_nan().get_val();
119+
}
120+
116121
if (x_abs == 0x7f80'0000U) {
117122
fputil::set_errno_if_required(EDOM);
118123
fputil::raise_except_if_required(FE_INVALID);

libc/src/math/generic/tanf16.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ LLVM_LIBC_FUNCTION(float16, tanf16, (float16 x)) {
8484

8585
// tan(+/-inf) = NaN, and tan(NaN) = NaN
8686
if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
87+
if (xbits.is_signaling_nan()) {
88+
fputil::raise_except_if_required(FE_INVALID);
89+
return FPBits::quiet_nan().get_val();
90+
}
8791
// x = +/-inf
8892
if (x_abs == 0x7c00) {
8993
fputil::set_errno_if_required(EDOM);

libc/src/math/generic/tanpif16.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ LLVM_LIBC_FUNCTION(float16, tanpif16, (float16 x)) {
6363
if (LIBC_UNLIKELY(x_abs >= 0x6400)) {
6464
// Check for NaN or infinity values
6565
if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
66+
if (xbits.is_signaling_nan()) {
67+
fputil::raise_except_if_required(FE_INVALID);
68+
return FPBits::quiet_nan().get_val();
69+
}
70+
// is inf
6671
if (x_abs == 0x7c00) {
6772
fputil::set_errno_if_required(EDOM);
6873
fputil::raise_except_if_required(FE_INVALID);

0 commit comments

Comments
 (0)