8000 gh-104263: Rely on Py_NAN and introduce Py_INFINITY by seberg · Pull Request #104202 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-104263: Rely on Py_NAN and introduce Py_INFINITY #104202

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 10 commits into from
May 10, 2023
Merged
Next Next commit
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 numpy/numpy#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?!
  • Loading branch information
seberg committed May 5, 2023
commit c88a6fb4e3ff7e7376c33f95f441ea509c69ef46
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
62 changes: 10 additions & 52 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);
10000 Expand Down Expand Up @@ -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) {
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_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
Expand Down
39 changes: 7 additions & 32 deletions Modules/mathmodule.c
10000
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,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
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
0