From c88a6fb4e3ff7e7376c33f95f441ea509c69ef46 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Fri, 5 May 2023 17:22:10 +0200 Subject: [PATCH 01/10] MAINT: Rely on Py_NAN and Py_HUGE_VAL being correctly defined It seems to me code all around relies on both being correct anyway. The actual value for Py_NAN is subtly incorrect on MIPS (depending on settings) or at least nonstandard, which seems to confuse some builtin functions. (Probably it is signalling, but NumPy saw this with fmin, which probably should also ignore signalling NaNs, see also https://github.com/numpy/numpy/issues/23158). The guards about `_PY_SHORT_FLOAT_REPR` making sense are relatively unrelated to NAN and INF being available. Nevertheless, I currently hide the Py_NAN definition if that is not set, since I am not sure what good alternative there is to be certain that Py_NAN is well defined. OTOH, I do suspect there is no platform where it is not and it should probably be changed?! --- Include/internal/pycore_dtoa.h | 2 -- Modules/cmathmodule.c | 62 ++++++---------------------------- Modules/mathmodule.c | 39 ++++----------------- Objects/floatobject.c | 11 ------ Python/dtoa.c | 34 ------------------- Python/pystrtod.c | 39 --------------------- 6 files changed, 17 insertions(+), 170 deletions(-) diff --git a/Include/internal/pycore_dtoa.h b/Include/internal/pycore_dtoa.h index fb524770efed7c..4d9681d59a64f7 100644 --- a/Include/internal/pycore_dtoa.h +++ b/Include/internal/pycore_dtoa.h @@ -64,8 +64,6 @@ PyAPI_FUNC(double) _Py_dg_strtod(const char *str, char **ptr); PyAPI_FUNC(char *) _Py_dg_dtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve); PyAPI_FUNC(void) _Py_dg_freedtoa(char *s); -PyAPI_FUNC(double) _Py_dg_stdnan(int sign); -PyAPI_FUNC(double) _Py_dg_infinity(int sign); #endif // _PY_SHORT_FLOAT_REPR == 1 diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index b4f7e5424b4ccf..7256da7ef30fff 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -8,7 +8,6 @@ #include "Python.h" #include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR -#include "pycore_dtoa.h" // _Py_dg_stdnan() /* we need DBL_MAX, DBL_MIN, DBL_EPSILON, DBL_MANT_DIG and FLT_RADIX from float.h. We assume that FLT_RADIX is either 2 or 16. */ #include @@ -88,53 +87,6 @@ else { #endif #define CM_SCALE_DOWN (-(CM_SCALE_UP+1)/2) -/* Constants cmath.inf, cmath.infj, cmath.nan, cmath.nanj. - cmath.nan and cmath.nanj are defined only when either - _PY_SHORT_FLOAT_REPR is 1 (which should be - the most common situation on machines using an IEEE 754 - representation), or Py_NAN is defined. */ - -static double -m_inf(void) -{ -#if _PY_SHORT_FLOAT_REPR == 1 - return _Py_dg_infinity(0); -#else - return Py_HUGE_VAL; -#endif -} - -static Py_complex -c_infj(void) -{ - Py_complex r; - r.real = 0.0; - r.imag = m_inf(); - return r; -} - -#if _PY_SHORT_FLOAT_REPR == 1 - -static double -m_nan(void) -{ -#if _PY_SHORT_FLOAT_REPR == 1 - return _Py_dg_stdnan(0); -#else - return Py_NAN; -#endif -} - -static Py_complex -c_nanj(void) -{ - Py_complex r; - r.real = 0.0; - r.imag = m_nan(); - return r; -} - -#endif /* forward declarations */ static Py_complex cmath_asinh_impl(PyObject *, Py_complex); @@ -1274,20 +1226,26 @@ cmath_exec(PyObject *mod) if (PyModule_AddObject(mod, "tau", PyFloat_FromDouble(Py_MATH_TAU)) < 0) { return -1; } - if (PyModule_AddObject(mod, "inf", PyFloat_FromDouble(m_inf())) < 0) { + if (PyModule_AddObject(mod, "inf", PyFloat_FromDouble(Py_HUGE_VAL)) < 0) { return -1; } + Py_complex infj = {0.0, Py_HUGE_VAL}; if (PyModule_AddObject(mod, "infj", - PyComplex_FromCComplex(c_infj())) < 0) { + PyComplex_FromCComplex(infj)) < 0) { return -1; } #if _PY_SHORT_FLOAT_REPR == 1 - if (PyModule_AddObject(mod, "nan", PyFloat_FromDouble(m_nan())) < 0) { + /* + * NaN exposure is guarded by having IEEE doubles via _PY_SHORT_FLOAT_REPR. + * This is probably an overly restrictive guard. + */ + if (PyModule_AddObject(mod, "nan", PyFloat_FromDouble(Py_NAN)) < 0) { return -1; } + Py_complex nanj = {0.0, Py_NAN}; if (PyModule_AddObject(mod, "nanj", - PyComplex_FromCComplex(c_nanj())) < 0) { + PyComplex_FromCComplex(nanj)) < 0) { return -1; } #endif diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 4a2381d9611776..0d62b66ed0dd8a 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -59,7 +59,6 @@ raised for division by zero and mod by zero. #include "Python.h" #include "pycore_bitutils.h" // _Py_bit_length() #include "pycore_call.h" // _PyObject_CallNoArgs() -#include "pycore_dtoa.h" // _Py_dg_infinity() #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_object.h" // _PyObject_LookupSpecial() @@ -389,34 +388,6 @@ lanczos_sum(double x) return num/den; } -/* Constant for +infinity, generated in the same way as float('inf'). */ - -static double -m_inf(void) -{ -#if _PY_SHORT_FLOAT_REPR == 1 - return _Py_dg_infinity(0); -#else - return Py_HUGE_VAL; -#endif -} - -/* Constant nan value, generated in the same way as float('nan'). */ -/* We don't currently assume that Py_NAN is defined everywhere. */ - -#if _PY_SHORT_FLOAT_REPR == 1 - -static double -m_nan(void) -{ -#if _PY_SHORT_FLOAT_REPR == 1 - return _Py_dg_stdnan(0); -#else - return Py_NAN; -#endif -} - -#endif static double m_tgamma(double x) @@ -3938,7 +3909,7 @@ math_ulp_impl(PyObject *module, double x) if (Py_IS_INFINITY(x)) { return x; } - double inf = m_inf(); + double inf = Py_HUGE_VAL; double x2 = nextafter(x, inf); if (Py_IS_INFINITY(x2)) { /* special case: x is the largest positive representable float */ @@ -3975,11 +3946,15 @@ math_exec(PyObject *module) if (PyModule_AddObject(module, "tau", PyFloat_FromDouble(Py_MATH_TAU)) < 0) { return -1; } - if (PyModule_AddObject(module, "inf", PyFloat_FromDouble(m_inf())) < 0) { + if (PyModule_AddObject(module, "inf", PyFloat_FromDouble(Py_HUGE_VAL)) < 0) { return -1; } #if _PY_SHORT_FLOAT_REPR == 1 - if (PyModule_AddObject(module, "nan", PyFloat_FromDouble(m_nan())) < 0) { + /* + * NaN exposure is guarded by having IEEE doubles via _PY_SHORT_FLOAT_REPR. + * This is probably an overly restrictive guard. + */ + if (PyModule_AddObject(module, "nan", PyFloat_FromDouble(Py_NAN)) < 0) { return -1; } #endif diff --git a/Objects/floatobject.c b/Objects/floatobject.c index d257857d9c619c..f9fbbdc34e564a 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -2424,7 +2424,6 @@ PyFloat_Unpack2(const char *data, int le) f |= *p; if (e == 0x1f) { -#if _PY_SHORT_FLOAT_REPR == 0 if (f == 0) { /* Infinity */ return sign ? -Py_HUGE_VAL : Py_HUGE_VAL; @@ -2433,16 +2432,6 @@ PyFloat_Unpack2(const char *data, int le) /* NaN */ return sign ? -Py_NAN : Py_NAN; } -#else // _PY_SHORT_FLOAT_REPR == 1 - if (f == 0) { - /* Infinity */ - return _Py_dg_infinity(sign); - } - else { - /* NaN */ - return _Py_dg_stdnan(sign); - } -#endif // _PY_SHORT_FLOAT_REPR == 1 } x = (double)f / 1024.0; diff --git a/Python/dtoa.c b/Python/dtoa.c index 6ea60ac9946e0f..c5e343b82f74c5 100644 --- a/Python/dtoa.c +++ b/Python/dtoa.c @@ -273,11 +273,6 @@ typedef union { double d; ULong L[2]; } U; #define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) #define Big1 0xffffffff -/* Standard NaN used by _Py_dg_stdnan. */ - -#define NAN_WORD0 0x7ff80000 -#define NAN_WORD1 0 - /* Bits of the representation of positive infinity. */ #define POSINF_WORD0 0x7ff00000 @@ -1399,35 +1394,6 @@ bigcomp(U *rv, const char *s0, BCinfo *bc) return 0; } -/* Return a 'standard' NaN value. - - There are exactly two quiet NaNs that don't arise by 'quieting' signaling - NaNs (see IEEE 754-2008, section 6.2.1). If sign == 0, return the one whose - sign bit is cleared. Otherwise, return the one whose sign bit is set. -*/ - -double -_Py_dg_stdnan(int sign) -{ - U rv; - word0(&rv) = NAN_WORD0; - word1(&rv) = NAN_WORD1; - if (sign) - word0(&rv) |= Sign_bit; - return dval(&rv); -} - -/* Return positive or negative infinity, according to the given sign (0 for - * positive infinity, 1 for negative infinity). */ - -double -_Py_dg_infinity(int sign) -{ - U rv; - word0(&rv) = POSINF_WORD0; - word1(&rv) = POSINF_WORD1; - return sign ? -dval(&rv) : dval(&rv); -} double _Py_dg_strtod(const char *s00, char **se) diff --git a/Python/pystrtod.c b/Python/pystrtod.c index d77b846f0403f0..1cdc3479594e9e 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -23,44 +23,6 @@ case_insensitive_match(const char *s, const char *t) return the NaN or Infinity as a double and set *endptr to point just beyond the successfully parsed portion of the string. On failure, return -1.0 and set *endptr to point to the start of the string. */ - -#if _PY_SHORT_FLOAT_REPR == 1 - -double -_Py_parse_inf_or_nan(const char *p, char **endptr) -{ - double retval; - const char *s; - int negate = 0; - - s = p; - if (*s == '-') { - negate = 1; - s++; - } - else if (*s == '+') { - s++; - } - if (case_insensitive_match(s, "inf")) { - s += 3; - if (case_insensitive_match(s, "inity")) - s += 5; - retval = _Py_dg_infinity(negate); - } - else if (case_insensitive_match(s, "nan")) { - s += 3; - retval = _Py_dg_stdnan(negate); - } - else { - s = p; - retval = -1.0; - } - *endptr = (char *)s; - return retval; -} - -#else - double _Py_parse_inf_or_nan(const char *p, char **endptr) { @@ -94,7 +56,6 @@ _Py_parse_inf_or_nan(const char *p, char **endptr) return retval; } -#endif /** * _PyOS_ascii_strtod: From 230882ca8cdde12618b3a8b729b5d6a9f7fa3d96 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Sun, 7 May 2023 14:26:05 +0200 Subject: [PATCH 02/10] MAINT: Introduce and use `Py_INFINIY`; rely on NaN being defined We can rely on both as Python now forces IEEE compliance and C99 so that both should always be well defined and there is no need for `math.nan` not being defined. --- Include/pymath.h | 16 +++++++++------- Modules/cmathmodule.c | 11 ++--------- Modules/mathmodule.c | 8 +------- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/Include/pymath.h b/Include/pymath.h index 772b67e4977563..b8020bda6bd8fb 100644 --- a/Include/pymath.h +++ b/Include/pymath.h @@ -39,18 +39,20 @@ // Return 1 if float or double arg is neither infinite nor NAN, else 0. #define Py_IS_FINITE(X) isfinite(X) -/* HUGE_VAL is supposed to expand to a positive double infinity. Python - * uses Py_HUGE_VAL instead because some platforms are broken in this - * respect. We used to embed code in pyport.h to try to worm around that, - * but different platforms are broken in conflicting ways. If you're on - * a platform where HUGE_VAL is defined incorrectly, fiddle your Python - * config to #define Py_HUGE_VAL to something that works on your platform. +// Py_INFINITY: Value that evaluates to a positive double infinity. +#ifndef Py_INFINITY +# define Py_INFINITY ((double)INFINITY) +#endif + +/* Py_HUGE_VAL should always be the same as Py_INFINITY. But historically + * this was not reliable and Python did not require IEEE floats and C99 + * conformity. Prefer Py_INFINITY for new code. */ #ifndef Py_HUGE_VAL # define Py_HUGE_VAL HUGE_VAL #endif -// Py_NAN: Value that evaluates to a quiet Not-a-Number (NaN). +// Py_NAN: Value that evaluates to a quiet and positive Not-a-Number (NaN). #if !defined(Py_NAN) # if _Py__has_builtin(__builtin_nan) // Built-in implementation of the ISO C99 function nan(): quiet NaN. diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 7256da7ef30fff..9d2ab73e323609 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -1230,25 +1230,18 @@ cmath_exec(PyObject *mod) return -1; } - Py_complex infj = {0.0, Py_HUGE_VAL}; + Py_complex infj = {0.0, Py_INFINITY}; if (PyModule_AddObject(mod, "infj", PyComplex_FromCComplex(infj)) < 0) { return -1; } -#if _PY_SHORT_FLOAT_REPR == 1 - /* - * NaN exposure is guarded by having IEEE doubles via _PY_SHORT_FLOAT_REPR. - * This is probably an overly restrictive guard. - */ if (PyModule_AddObject(mod, "nan", PyFloat_FromDouble(Py_NAN)) < 0) { return -1; } Py_complex nanj = {0.0, Py_NAN}; - if (PyModule_AddObject(mod, "nanj", - PyComplex_FromCComplex(nanj)) < 0) { + if (PyModule_AddObject(mod, "nanj", PyComplex_FromCComplex(nanj)) < 0) { return -1; } -#endif /* initialize special value tables */ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 0d62b66ed0dd8a..cf2ce890bc8e2c 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -3946,18 +3946,12 @@ math_exec(PyObject *module) if (PyModule_AddObject(module, "tau", PyFloat_FromDouble(Py_MATH_TAU)) < 0) { return -1; } - if (PyModule_AddObject(module, "inf", PyFloat_FromDouble(Py_HUGE_VAL)) < 0) { + if (PyModule_AddObject(module, "inf", PyFloat_FromDouble(Py_INFINITY)) < 0) { return -1; } -#if _PY_SHORT_FLOAT_REPR == 1 - /* - * NaN exposure is guarded by having IEEE doubles via _PY_SHORT_FLOAT_REPR. - * This is probably an overly restrictive guard. - */ if (PyModule_AddObject(module, "nan", PyFloat_FromDouble(Py_NAN)) < 0) { return -1; } -#endif return 0; } From fd3943708359783597ea01427494f3d5f51cc0fd Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Sun, 7 May 2023 15:18:01 +0200 Subject: [PATCH 03/10] TST: Add some tests around (c)math.nan and nan parsing signs --- Lib/test/test_cmath.py | 5 +++++ Lib/test/test_complex.py | 7 +++++++ Lib/test/test_float.py | 5 +---- Lib/test/test_math.py | 4 ++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_cmath.py b/Lib/test/test_cmath.py index cd2c6939105d40..57f80d5d8cd016 100644 --- a/Lib/test/test_cmath.py +++ b/Lib/test/test_cmath.py @@ -166,6 +166,11 @@ def test_infinity_and_nan_constants(self): self.assertEqual(cmath.nan.imag, 0.0) self.assertEqual(cmath.nanj.real, 0.0) self.assertTrue(math.isnan(cmath.nanj.imag)) + # Also check that the sign of all of these is positive: + self.assertEqual(math.copysign(1., cmath.nan.real), 1.) + self.assertEqual(math.copysign(1., cmath.nan.imag), 1.) + self.assertEqual(math.copysign(1., cmath.nanj.real), 1.) + self.assertEqual(math.copysign(1., cmath.nanj.imag), 1.) # Check consistency with reprs. self.assertEqual(repr(cmath.inf), "inf") diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 51ba151505fb54..9180cca62b28b8 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -529,6 +529,12 @@ class complex2(complex): self.assertFloatsAreIdentical(z.real, x) self.assertFloatsAreIdentical(z.imag, y) + def test_constructor_negative_nans_from_string(self): + self.assertEqual(copysign(1., complex("-nan").real), -1.) + self.assertEqual(copysign(1., complex("-nanj").imag), -1.) + self.assertEqual(copysign(1., complex("-nan-nanj").real), -1.) + self.assertEqual(copysign(1., complex("-nan-nanj").imag), -1.) + def test_underscores(self): # check underscores for lit in VALID_UNDERSCORE_LITERALS: @@ -569,6 +575,7 @@ def test(v, expected, test_fn=self.assertEqual): test(complex(NAN, 1), "(nan+1j)") test(complex(1, NAN), "(1+nanj)") test(complex(NAN, NAN), "(nan+nanj)") + test(complex(-NAN, -NAN), "(nan+nanj)") test(complex(0, INF), "infj") test(complex(0, -INF), "-infj") diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index f8350c1e4caa27..c4ee1e08251d63 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -1040,11 +1040,8 @@ def test_inf_signs(self): self.assertEqual(copysign(1.0, float('inf')), 1.0) self.assertEqual(copysign(1.0, float('-inf')), -1.0) - @unittest.skipUnless(getattr(sys, 'float_repr_style', '') == 'short', - "applies only when using short float repr style") def test_nan_signs(self): - # When using the dtoa.c code, the sign of float('nan') should - # be predictable. + # The sign of float('nan') should be predictable. self.assertEqual(copysign(1.0, float('nan')), 1.0) self.assertEqual(copysign(1.0, float('-nan')), -1.0) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 433161c2dd4145..f282434c9a3359 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1881,11 +1881,11 @@ def testIsinf(self): self.assertFalse(math.isinf(0.)) self.assertFalse(math.isinf(1.)) - @requires_IEEE_754 def test_nan_constant(self): + # `math.nan` must be a quiet NaN with positive sign bit self.assertTrue(math.isnan(math.nan)) + self.assertEqual(math.copysign(1., math.nan), 1.) - @requires_IEEE_754 def test_inf_constant(self): self.assertTrue(math.isinf(math.inf)) self.assertGreater(math.inf, 0.0) From 1e42249630acb7b512f3e5dbcf97a360b94be0b2 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Mon, 8 May 2023 10:49:41 +0200 Subject: [PATCH 04/10] Change Py_HUGE_VAL to Py_INFINITY where I had missed/forgotten it --- Modules/cmathmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 9d2ab73e323609..8400a4a9883a85 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -1226,7 +1226,7 @@ cmath_exec(PyObject *mod) if (PyModule_AddObject(mod, "tau", PyFloat_FromDouble(Py_MATH_TAU)) < 0) { return -1; } - if (PyModule_AddObject(mod, "inf", PyFloat_FromDouble(Py_HUGE_VAL)) < 0) { + if (PyModule_AddObject(mod, "inf", PyFloat_FromDouble(Py_INFINITY)) < 0) { return -1; } From efd67bc7bfb194c672190f7d755087f9b3ce3456 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 8 May 2023 10:35:05 +0100 Subject: [PATCH 05/10] Add news entry --- .../2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst new file mode 100644 index 00000000000000..d7886e3097e9ef --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst @@ -0,0 +1,6 @@ +Fix ``float("nan")`` to produce a quiet NaN on platforms (like MIPS) where +the meaning of the signalling / quiet bit is inverted from its usual +meaning. Also introduce a new macro ``Py_INFINITY`` matching C99's +``INFINITY``, and refactor internals to rely on C99's ``NAN`` and +``INFINITY`` macros instead of hard-coding bit patterns for infinities and +NaNs. From e15236de1eda28486adb69518638cd6e773ad915 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 8 May 2023 10:35:26 +0100 Subject: [PATCH 06/10] One more Py_HUGE_VAL -> Py_INFINITY replacement --- Modules/mathmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index cf2ce890bc8e2c..ea68150582252e 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -406,7 +406,7 @@ m_tgamma(double x) if (x == 0.0) { errno = EDOM; /* tgamma(+-0.0) = +-inf, divide-by-zero */ - return copysign(Py_HUGE_VAL, x); + return copysign(Py_INFINITY, x); } /* integer arguments */ From 18a05cb19c3d5ffa64cae2c6425fb128c3cea230 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 8 May 2023 10:38:56 +0100 Subject: [PATCH 07/10] And another Py_HUGE_VAL -> Py_INFINITY replacement --- Modules/mathmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index ea68150582252e..6b65bd8242b190 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -3909,7 +3909,7 @@ math_ulp_impl(PyObject *module, double x) if (Py_IS_INFINITY(x)) { return x; } - double inf = Py_HUGE_VAL; + double inf = Py_INFINITY; double x2 = nextafter(x, inf); if (Py_IS_INFINITY(x2)) { /* special case: x is the largest positive representable float */ From fabf7d38354d793f52289caa1213e8e9f49728c9 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 8 May 2023 10:39:23 +0100 Subject: [PATCH 08/10] Add acknowledgement in news entry --- .../2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst index d7886e3097e9ef..987c1850d12a5c 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst @@ -3,4 +3,4 @@ the meaning of the signalling / quiet bit is inverted from its usual meaning. Also introduce a new macro ``Py_INFINITY`` matching C99's ``INFINITY``, and refactor internals to rely on C99's ``NAN`` and ``INFINITY`` macros instead of hard-coding bit patterns for infinities and -NaNs. +NaNs. Thanks Sebastien Berg. From e8c36a224b4d223a22e67c76cd2419c633fd6e6d Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 8 May 2023 10:42:09 +0100 Subject: [PATCH 09/10] Fix spelling of Sebastian's name. --- .../2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst index 987c1850d12a5c..342467cfcd4e75 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-08-10-34-55.gh-issue-104263.ctHWI8.rst @@ -3,4 +3,4 @@ the meaning of the signalling / quiet bit is inverted from its usual meaning. Also introduce a new macro ``Py_INFINITY`` matching C99's ``INFINITY``, and refactor internals to rely on C99's ``NAN`` and ``INFINITY`` macros instead of hard-coding bit patterns for infinities and -NaNs. Thanks Sebastien Berg. +NaNs. Thanks Sebastian Berg. From a35ae197c42642d4b29ae0a9c9a34c434d5fde96 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Tue, 9 May 2023 10:47:42 +0200 Subject: [PATCH 10/10] Rely on C99 NAN being defined and ensure NaN sign for creation functions --- Include/pymath.h | 11 +++-------- Modules/cmathmodule.c | 4 ++-- Modules/mathmodule.c | 2 +- Objects/floatobject.c | 2 +- Python/pystrtod.c | 2 +- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Include/pymath.h b/Include/pymath.h index b8020bda6bd8fb..4c1e3d9984894b 100644 --- a/Include/pymath.h +++ b/Include/pymath.h @@ -52,16 +52,11 @@ # define Py_HUGE_VAL HUGE_VAL #endif -// Py_NAN: Value that evaluates to a quiet and positive Not-a-Number (NaN). +/* Py_NAN: Value that evaluates to a quiet Not-a-Number (NaN). The sign is + * undefined and normally not relevant, but e.g. fixed for float("nan"). + */ #if !defined(Py_NAN) -# if _Py__has_builtin(__builtin_nan) - // Built-in implementation of the ISO C99 function nan(): quiet NaN. -# define Py_NAN (__builtin_nan("")) -#else - // Use C99 NAN constant: quiet Not-A-Number. - // NAN is a float, Py_NAN is a double: cast to double. # define Py_NAN ((double)NAN) -# endif #endif #endif /* Py_PYMATH_H */ diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 8400a4a9883a85..e2fc2c93fb1443 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -1235,10 +1235,10 @@ cmath_exec(PyObject *mod) PyComplex_FromCComplex(infj)) < 0) { return -1; } - if (PyModule_AddObject(mod, "nan", PyFloat_FromDouble(Py_NAN)) < 0) { + if (PyModule_AddObject(mod, "nan", PyFloat_FromDouble(fabs(Py_NAN))) < 0) { return -1; } - Py_complex nanj = {0.0, Py_NAN}; + Py_complex nanj = {0.0, fabs(Py_NAN)}; if (PyModule_AddObject(mod, "nanj", PyComplex_FromCComplex(nanj)) < 0) { return -1; } diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 6b65bd8242b190..93edb2882acbf4 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -3949,7 +3949,7 @@ math_exec(PyObject *module) if (PyModule_AddObject(module, "inf", PyFloat_FromDouble(Py_INFINITY)) < 0) { return -1; } - if (PyModule_AddObject(module, "nan", PyFloat_FromDouble(Py_NAN)) < 0) { + if (PyModule_AddObject(module, "nan", PyFloat_FromDouble(fabs(Py_NAN))) < 0) { return -1; } return 0; diff --git a/Objects/floatobject.c b/Objects/floatobject.c index f9fbbdc34e564a..83a263c0d9c67e 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -2430,7 +2430,7 @@ PyFloat_Unpack2(const char *data, int le) } else { /* NaN */ - return sign ? -Py_NAN : Py_NAN; + return sign ? -fabs(Py_NAN) : fabs(Py_NAN); } } diff --git a/Python/pystrtod.c b/Python/pystrtod.c index 1cdc3479594e9e..9bb060e3d11979 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -46,7 +46,7 @@ _Py_parse_inf_or_nan(const char *p, char **endptr) } else if (case_insensitive_match(s, "nan")) { s += 3; - retval = negate ? -Py_NAN : Py_NAN; + retval = negate ? -fabs(Py_NAN) : fabs(Py_NAN); } else { s = p;