Skip to content

Commit 941b1bb

Browse files
committed
gh-70450: Avoid floating-point exceptions during imports
1 parent 5f00a02 commit 941b1bb

5 files changed

Lines changed: 82 additions & 75 deletions

File tree

Lib/test/test_cmath.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,20 @@ def test_constants(self):
124124
self.assertAlmostEqual(cmath.e, e_expected, places=9,
125125
msg="cmath.e is {}; should be {}".format(cmath.e, e_expected))
126126

127+
def test_precomputed_constants(self):
128+
self.assertEqual(
129+
cmath.acos(complex(math.inf, math.inf)).real,
130+
math.pi / 4,
131+
)
132+
self.assertEqual(
133+
cmath.acos(complex(0.0, math.inf)).real,
134+
math.pi / 2,
135+
)
136+
self.assertEqual(
137+
cmath.acos(complex(-math.inf, math.inf)).real,
138+
math.pi * 3 / 4,
139+
)
140+
127141
def test_infinity_and_nan_constants(self):
128142
self.assertEqual(cmath.inf.real, math.inf)
129143
self.assertEqual(cmath.inf.imag, 0.0)

Lib/test/test_math.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ def testCosh(self):
479479

480480
def testDegrees(self):
481481
self.assertRaises(TypeError, math.degrees)
482+
self.assertEqual(math.degrees(1.0), 180.0 / math.pi)
482483
self.ftest('degrees(pi)', math.degrees(math.pi), 180.0)
483484
self.ftest('degrees(pi/2)', math.degrees(math.pi/2), 90.0)
484485
self.ftest('degrees(-pi/4)', math.degrees(-math.pi/4), -45.0)
@@ -1748,6 +1749,7 @@ def testPow(self):
17481749

17491750
def testRadians(self):
17501751
self.assertRaises(TypeError, math.radians)
1752+
self.assertEqual(math.radians(1.0), math.pi / 180.0)
17511753
self.ftest('radians(180)', math.radians(180), math.pi)
17521754
self.ftest('radians(90)', math.radians(90), math.pi/2)
17531755
self.ftest('radians(-45)', math.radians(-45), -math.pi/4)

Modules/cmathmodule.c

Lines changed: 58 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
#include "Python.h"
1010
#include "pycore_complexobject.h" // _Py_c_neg()
11-
#include "pycore_lock.h" // _PyOnceFlag_CallOnce()
1211
#include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR
1312
/* we need DBL_MAX, DBL_MIN, DBL_EPSILON, DBL_MANT_DIG and FLT_RADIX from
1413
float.h. We assume that FLT_RADIX is either 2 or 16. */
@@ -140,21 +139,40 @@ special_type(double d)
140139
return ST_NINF;
141140
}
142141

142+
#define P Py_MATH_PI
143+
/* Precomputed for static initialization with MSVC /fp:strict. */
144+
#define P14 0.78539816339744830962 /* P/4: 0x3fe921fb54442d18 */
145+
#define P12 1.57079632679489661923 /* P/2: 0x3ff921fb54442d18 */
146+
#define P34 2.35619449019234492885 /* P*3/4: 0x4002d97c7f3321d2 */
147+
#define INF INFINITY
148+
/* MSVC /fp:strict does not accept NAN in static initializers. This finite
149+
value does not otherwise occur in the tables and is decoded when read. */
150+
#define N 9.5426319407711027e33
151+
#define U -9.5426319407711027e33 /* unlikely value, used as placeholder */
152+
153+
static Py_complex
154+
special_value(const Py_complex table[7][7],
155+
enum special_types real_type,
156+
enum special_types imag_type)
157+
{
158+
Py_complex value = table[real_type][imag_type];
159+
if (value.real == N) {
160+
value.real = fabs(nan(""));
161+
}
162+
if (value.imag == N) {
163+
value.imag = fabs(nan(""));
164+
}
165+
return value;
166+
}
167+
143168
#define SPECIAL_VALUE(z, table) \
144169
if (!isfinite((z).real) || !isfinite((z).imag)) { \
145170
errno = 0; \
146-
return table[special_type((z).real)] \
147-
[special_type((z).imag)]; \
171+
return special_value( \
172+
table, special_type((z).real), \
173+
special_type((z).imag)); \
148174
}
149175

150-
#define P Py_MATH_PI
151-
#define P14 0.78539816339744830962
152-
#define P12 1.57079632679489661923
153-
#define P34 2.35619449019234492885
154-
#define INF INFINITY
155-
#define N 9.5426319407711027e33 /* finite placeholder for NaN */
156-
#define U -9.5426319407711027e33 /* unlikely value, used as placeholder */
157-
158176
/* First, the C functions that do the real work. Each of the c_*
159177
functions computes and returns the C99 Annex G recommended result
160178
and also sets errno as follows: errno = 0 if no floating-point
@@ -164,7 +182,7 @@ special_type(double d)
164182
raised.
165183
*/
166184

167-
static Py_complex acos_special_values[7][7] = {
185+
static const Py_complex acos_special_values[7][7] = {
168186
{ {P34,INF}, {P,INF}, {P,INF}, {P,-INF}, {P,-INF}, {P34,-INF}, {N,INF} },
169187
{ {P12,INF}, {U,U}, {U,U}, {U,U}, {U,U}, {P12,-INF}, {N,N} },
170188
{ {P12,INF}, {U,U}, {P12,0.}, {P12,-0.}, {U,U}, {P12,-INF}, {P12,N} },
@@ -210,7 +228,7 @@ cmath_acos_impl(PyObject *module, Py_complex z)
210228
}
211229

212230

213-
static Py_complex acosh_special_values[7][7] = {
231+
static const Py_complex acosh_special_values[7][7] = {
214232
{ {INF,-P34}, {INF,-P}, {INF,-P}, {INF,P}, {INF,P}, {INF,P34}, {INF,N} },
215233
{ {INF,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {INF,P12}, {N,N} },
216234
{ {INF,-P12}, {U,U}, {0.,-P12}, {0.,P12}, {U,U}, {INF,P12}, {N,P12} },
@@ -273,7 +291,7 @@ cmath_asin_impl(PyObject *module, Py_complex z)
273291
}
274292

275293

276-
static Py_complex asinh_special_values[7][7] = {
294+
static const Py_complex asinh_special_values[7][7] = {
277295
{ {-INF,-P14}, {-INF,-0.}, {-INF,-0.}, {-INF,0.}, {-INF,0.}, {-INF,P14}, {-INF,N} },
278296
{ {-INF,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {-INF,P12}, {N,N} },
279297
{ {-INF,-P12}, {U,U}, {-0.,-0.}, {-0.,0.}, {U,U}, {-INF,P12}, {N,N} },
@@ -342,7 +360,7 @@ cmath_atan_impl(PyObject *module, Py_complex z)
342360
}
343361

344362

345-
static Py_complex atanh_special_values[7][7] = {
363+
static const Py_complex atanh_special_values[7][7] = {
346364
{ {-0.,-P12}, {-0.,-P12}, {-0.,-P12}, {-0.,P12}, {-0.,P12}, {-0.,P12}, {-0.,N} },
347365
{ {-0.,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {-0.,P12}, {N,N} },
348366
{ {-0.,-P12}, {U,U}, {-0.,-0.}, {-0.,0.}, {U,U}, {-0.,P12}, {-0.,N} },
@@ -423,7 +441,7 @@ cmath_cos_impl(PyObject *module, Py_complex z)
423441

424442

425443
/* cosh(infinity + i*y) needs to be dealt with specially */
426-
static Py_complex cosh_special_values[7][7] = {
444+
static const Py_complex cosh_special_values[7][7] = {
427445
{ {INF,N}, {U,U}, {INF,0.}, {INF,-0.}, {U,U}, {INF,N}, {INF,N} },
428446
{ {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} },
429447
{ {N,0.}, {U,U}, {1.,0.}, {1.,-0.}, {U,U}, {N,0.}, {N,0.} },
@@ -460,8 +478,9 @@ cmath_cosh_impl(PyObject *module, Py_complex z)
460478
}
461479
}
462480
else {
463-
r = cosh_special_values[special_type(z.real)]
464-
[special_type(z.imag)];
481+
r = special_value(cosh_special_values,
482+
special_type(z.real),
483+
special_type(z.imag));
465484
}
466485
/* need to set errno = EDOM if y is +/- infinity and x is not
467486
a NaN */
@@ -493,7 +512,7 @@ cmath_cosh_impl(PyObject *module, Py_complex z)
493512

494513
/* exp(infinity + i*y) and exp(-infinity + i*y) need special treatment for
495514
finite y */
496-
static Py_complex exp_special_values[7][7] = {
515+
static const Py_complex exp_special_values[7][7] = {
497516
{ {0.,0.}, {U,U}, {0.,-0.}, {0.,0.}, {U,U}, {0.,0.}, {0.,0.} },
498517
{ {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} },
499518
{ {N,N}, {U,U}, {1.,-0.}, {1.,0.}, {U,U}, {N,N}, {N,N} },
@@ -529,8 +548,9 @@ cmath_exp_impl(PyObject *module, Py_complex z)
529548
}
530549
}
531550
else {
532-
r = exp_special_values[special_type(z.real)]
533-
[special_type(z.imag)];
551+
r = special_value(exp_special_values,
552+
special_type(z.real),
553+
special_type(z.imag));
534554
}
535555
/* need to set errno = EDOM if y is +/- infinity and x is not
536556
a NaN and not -infinity */
@@ -560,7 +580,7 @@ cmath_exp_impl(PyObject *module, Py_complex z)
560580
return r;
561581
}
562582

563-
static Py_complex log_special_values[7][7] = {
583+
static const Py_complex log_special_values[7][7] = {
564584
{ {INF,-P34}, {INF,-P}, {INF,-P}, {INF,P}, {INF,P}, {INF,P34}, {INF,N} },
565585
{ {INF,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {INF,P12}, {N,N} },
566586
{ {INF,-P12}, {U,U}, {-INF,-P}, {-INF,P}, {U,U}, {INF,P12}, {N,N} },
@@ -684,7 +704,7 @@ cmath_sin_impl(PyObject *module, Py_complex z)
684704

685705

686706
/* sinh(infinity + i*y) needs to be dealt with specially */
687-
static Py_complex sinh_special_values[7][7] = {
707+
static const Py_complex sinh_special_values[7][7] = {
688708
{ {INF,N}, {U,U}, {-INF,-0.}, {-INF,0.}, {U,U}, {INF,N}, {INF,N} },
689709
{ {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} },
690710
{ {0.,N}, {U,U}, {-0.,-0.}, {-0.,0.}, {U,U}, {0.,N}, {0.,N} },
@@ -722,8 +742,9 @@ cmath_sinh_impl(PyObject *module, Py_complex z)
722742
}
723743
}
724744
else {
725-
r = sinh_special_values[special_type(z.real)]
726-
[special_type(z.imag)];
745+
r = special_value(sinh_special_values,
746+
special_type(z.real),
747+
special_type(z.imag));
727748
}
728749
/* need to set errno = EDOM if y is +/- infinity and x is not
729750
a NaN */
@@ -751,7 +772,7 @@ cmath_sinh_impl(PyObject *module, Py_complex z)
751772
}
752773

753774

754-
static Py_complex sqrt_special_values[7][7] = {
775+
static const Py_complex sqrt_special_values[7][7] = {
755776
{ {INF,-INF}, {0.,-INF}, {0.,-INF}, {0.,INF}, {0.,INF}, {INF,INF}, {N,INF} },
756777
{ {INF,-INF}, {U,U}, {U,U}, {U,U}, {U,U}, {INF,INF}, {N,N} },
757778
{ {INF,-INF}, {U,U}, {0.,-0.}, {0.,0.}, {U,U}, {INF,INF}, {N,N} },
@@ -858,7 +879,7 @@ cmath_tan_impl(PyObject *module, Py_complex z)
858879

859880

860881
/* tanh(infinity + i*y) needs to be dealt with specially */
861-
static Py_complex tanh_special_values[7][7] = {
882+
static const Py_complex tanh_special_values[7][7] = {
862883
{ {-1.,0.}, {U,U}, {-1.,-0.}, {-1.,0.}, {U,U}, {-1.,0.}, {-1.,0.} },
863884
{ {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} },
864885
{ {-0.0,N}, {U,U}, {-0.,-0.}, {-0.,0.}, {U,U}, {-0.0,N}, {-0.,N} },
@@ -910,8 +931,9 @@ cmath_tanh_impl(PyObject *module, Py_complex z)
910931
}
911932
}
912933
else {
913-
r = tanh_special_values[special_type(z.real)]
914-
[special_type(z.imag)];
934+
r = special_value(tanh_special_values,
935+
special_type(z.real),
936+
special_type(z.imag));
915937
}
916938
/* need to set errno = EDOM if z.imag is +/-infinity and
917939
z.real is finite */
@@ -1050,7 +1072,7 @@ cmath_polar_impl(PyObject *module, Py_complex z)
10501072
10511073
*/
10521074

1053-
static Py_complex rect_special_values[7][7] = {
1075+
static const Py_complex rect_special_values[7][7] = {
10541076
{ {INF,N}, {U,U}, {-INF,0.}, {-INF,-0.}, {U,U}, {INF,N}, {INF,N} },
10551077
{ {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} },
10561078
{ {0.,0.}, {U,U}, {-0.,0.}, {-0.,-0.}, {U,U}, {0.,0.}, {0.,0.} },
@@ -1094,8 +1116,9 @@ cmath_rect_impl(PyObject *module, double r, double phi)
10941116
}
10951117
}
10961118
else {
1097-
z = rect_special_values[special_type(r)]
1098-
[special_type(phi)];
1119+
z = special_value(rect_special_values,
1120+
special_type(r),
1121+
special_type(phi));
10991122
}
11001123
/* need to set errno = EDOM if r is a nonzero number and phi
11011124
is infinite */
@@ -1231,41 +1254,6 @@ PyDoc_STRVAR(module_doc,
12311254
"This module provides access to mathematical functions for complex\n"
12321255
"numbers.");
12331256

1234-
static _PyOnceFlag special_values_init_once = {0};
1235-
1236-
static int
1237-
init_special_values(void *Py_UNUSED(arg))
1238-
{
1239-
Py_complex *tables[] = {
1240-
acos_special_values[0],
1241-
acosh_special_values[0],
1242-
asinh_special_values[0],
1243-
atanh_special_values[0],
1244-
cosh_special_values[0],
1245-
exp_special_values[0],
1246-
log_special_values[0],
1247-
sinh_special_values[0],
1248-
sqrt_special_values[0],
1249-
tanh_special_values[0],
1250-
rect_special_values[0],
1251-
};
1252-
1253-
/* MSVC with /fp:strict does not treat NAN as a constant expression.
1254-
Replace its finite placeholders once all static initialization is
1255-
complete. */
1256-
for (size_t i = 0; i < Py_ARRAY_LENGTH(tables); i++) {
1257-
for (size_t j = 0; j < 7 * 7; j++) {
1258-
if (tables[i][j].real == N) {
1259-
tables[i][j].real = Py_NAN;
1260-
}
1261-
if (tables[i][j].imag == N) {
1262-
tables[i][j].imag = Py_NAN;
1263-
}
1264-
}
1265-
}
1266-
return 0;
1267-
}
1268-
12691257
static PyMethodDef cmath_methods[] = {
12701258
CMATH_ACOS_METHODDEF
12711259
CMATH_ACOSH_METHODDEF
@@ -1296,10 +1284,6 @@ static PyMethodDef cmath_methods[] = {
12961284
static int
12971285
cmath_exec(PyObject *mod)
12981286
{
1299-
/* init cannot fail */
1300-
(void)_PyOnceFlag_CallOnce(
1301-
&special_values_init_once, init_special_values, NULL);
1302-
13031287
if (PyModule_Add(mod, "pi", PyFloat_FromDouble(Py_MATH_PI)) < 0) {
13041288
return -1;
13051289
}
@@ -1318,10 +1302,11 @@ cmath_exec(PyObject *mod)
13181302
if (PyModule_Add(mod, "infj", PyComplex_FromCComplex(infj)) < 0) {
13191303
return -1;
13201304
}
1321-
if (PyModule_Add(mod, "nan", PyFloat_FromDouble(fabs(Py_NAN))) < 0) {
1305+
double nan_value = fabs(nan(""));
1306+
if (PyModule_Add(mod, "nan", PyFloat_FromDouble(nan_value)) < 0) {
13221307
return -1;
13231308
}
1324-
Py_complex nanj = {0.0, fabs(Py_NAN)};
1309+
Py_complex nanj = {0.0, nan_value};
13251310
if (PyModule_Add(mod, "nanj", PyComplex_FromCComplex(nanj)) < 0) {
13261311
return -1;
13271312
}

Modules/mathmodule.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2610,7 +2610,10 @@ math_pow_impl(PyObject *module, double x, double y)
26102610
}
26112611

26122612

2613+
/* Precomputed for static initialization with MSVC /fp:strict. */
2614+
/* Py_MATH_PI / 180: binary64 0x3f91df46a2529d39 */
26132615
static const double degToRad = 0.017453292519943295769236907684886;
2616+
/* 180 / Py_MATH_PI: binary64 0x404ca5dc1a63c1f8 */
26142617
static const double radToDeg = 57.295779513082320876798154814105;
26152618

26162619
/*[clinic input]
@@ -3190,7 +3193,8 @@ math_exec(PyObject *module)
31903193
if (PyModule_Add(module, "inf", PyFloat_FromDouble(INFINITY)) < 0) {
31913194
return -1;
31923195
}
3193-
if (PyModule_Add(module, "nan", PyFloat_FromDouble(fabs(Py_NAN))) < 0) {
3196+
if (PyModule_Add(module, "nan",
3197+
PyFloat_FromDouble(fabs(nan("")))) < 0) {
31943198
return -1;
31953199
}
31963200

Python/dtoa.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1109,7 +1109,9 @@ static const double
11091109
bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };
11101110
static const double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128,
11111111
8.112963841460668e-225
1112-
/* = 2^106 * 1e-256 */
1112+
/* 2^106 * 1e-256, precomputed for
1113+
MSVC /fp:strict; binary64
1114+
0x1168062864ac6f43 */
11131115
};
11141116
/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */
11151117
/* flag unnecessarily. It leads to a song and dance at the end of strtod. */

0 commit comments

Comments
 (0)