Skip to content
Merged
2 changes: 0 additions & 2 deletions Include/internal/pycore_dtoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
16 changes: 9 additions & 7 deletions Include/pymath.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_cmath.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
7 changes: 7 additions & 0 deletions Lib/test/test_complex.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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")
Expand Down
5 changes: 1 addition & 4 deletions Lib/test/test_float.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_math.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
61 changes: 6 additions & 55 deletions Modules/cmathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <float.h>
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1274,23 +1226,22 @@ 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) {
Copy link
Member

Choose a reason for hiding this comment

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

Should probably use Py_INFINITY here, since we're changing this line anyway. (I don't want to clutter this PR by changing all instances of Py_HUGE_VAL to Py_INFINITY; that can happen in a separate cleanup PR.)

return -1;
}

Py_complex infj = {0.0, Py_INFINITY};
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) {
if (PyModule_AddObject(mod, "nan", PyFloat_FromDouble(Py_NAN)) < 0) {
return -1;
}
if (PyModule_AddObject(mod, "nanj",
PyComplex_FromCComplex(c_nanj())) < 0) {
Py_complex nanj = {0.0, Py_NAN};
if (PyModule_AddObject(mod, "nanj", PyComplex_FromCComplex(nanj)) < 0) {
return -1;
}
#endif

/* initialize special value tables */

Expand Down
37 changes: 3 additions & 34 deletions Modules/mathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -3975,14 +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(m_inf())) < 0) {
if (PyModule_AddObject(module, "inf", PyFloat_FromDouble(Py_INFINITY)) < 0) {
return -1;
}
#if _PY_SHORT_FLOAT_REPR == 1
if (PyModule_AddObject(module, "nan", PyFloat_FromDouble(m_nan())) < 0) {
if (PyModule_AddObject(module, "nan", PyFloat_FromDouble(Py_NAN)) < 0) {
return -1;
}
#endif
return 0;
}

Expand Down
11 changes: 0 additions & 11 deletions Objects/floatobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
34 changes: 0 additions & 34 deletions Python/dtoa.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
39 changes: 0 additions & 39 deletions Python/pystrtod.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -94,7 +56,6 @@ _Py_parse_inf_or_nan(const char *p, char **endptr)
return retval;
}

#endif

/**
* _PyOS_ascii_strtod:
Expand Down