Skip to content
49 changes: 0 additions & 49 deletions Include/internal/pycore_pymath.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,6 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif


/* _Py_ADJUST_ERANGE1(x)
* _Py_ADJUST_ERANGE2(x, y)
* Set errno to 0 before calling a libm function, and invoke one of these
* macros after, passing the function result(s) (_Py_ADJUST_ERANGE2 is useful
* for functions returning complex results). This makes two kinds of
* adjustments to errno: (A) If it looks like the platform libm set
* errno=ERANGE due to underflow, clear errno. (B) If it looks like the
* platform libm overflowed but didn't set errno, force errno to ERANGE. In
* effect, we're trying to force a useful implementation of C89 errno
* behavior.
* Caution:
* This isn't reliable. C99 no longer requires libm to set errno under
* any exceptional condition, but does require +- HUGE_VAL return
* values on overflow. A 754 box *probably* maps HUGE_VAL to a
* double infinity, and we're cool if that's so, unless the input
* was an infinity and an infinity is the expected result. A C89
* system sets errno to ERANGE, so we check for that too. We're
* out of luck if a C99 754 box doesn't map HUGE_VAL to +Inf, or
* if the returned result is a NaN, or if a C89 box returns HUGE_VAL
* in non-overflow cases.
*/
static inline void _Py_ADJUST_ERANGE1(double x)
{
if (errno == 0) {
if (x == INFINITY || x == -INFINITY) {
errno = ERANGE;
}
}
else if (errno == ERANGE && x == 0.0) {
errno = 0;
}
}

static inline void _Py_ADJUST_ERANGE2(double x, double y)
{
if (x == INFINITY || x == -INFINITY ||
y == INFINITY || y == -INFINITY)
{
if (errno == 0) {
errno = ERANGE;
}
}
else if (errno == ERANGE) {
errno = 0;
}
}


//--- HAVE_PY_SET_53BIT_PRECISION macro ------------------------------------
//
// The functions _Py_dg_strtod() and _Py_dg_dtoa() in Python/dtoa.c (which are
Expand Down
61 changes: 61 additions & 0 deletions Lib/test/test_complex.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ def assertClose(self, x, y, eps=1e-9):
# check that relative difference < eps
self.assertTrue(abs(x-y)/abs(y) < eps)

def assertSameSign(self, x, y):
if copysign(1., x) != copysign(1., y):
self.fail(f'{x!r} and {y!r} have different signs')

def check_div(self, x, y):
"""Compute complex z=x*y, and check that z/x==y and z/y==x."""
z = x * y
Expand Down Expand Up @@ -446,6 +450,63 @@ def test_pow_with_small_integer_exponents(self):
self.assertEqual(str(float_pow), str(int_pow))
self.assertEqual(str(complex_pow), str(int_pow))

# Check that complex numbers with special components
# are correctly handled.
values = [complex(x, y)
for x in [5, -5, +0.0, -0.0, INF, -INF, NAN]
for y in [12, -12, +0.0, -0.0, INF, -INF, NAN]]
for c in values:
with self.subTest(value=c):
self.assertComplexesAreIdentical(c**0, complex(1, +0.0))
self.assertComplexesAreIdentical(c**1, c)
self.assertComplexesAreIdentical(c**2, c*c)
self.assertComplexesAreIdentical(c**3, c*(c*c))
self.assertComplexesAreIdentical(c**3, (c*c)*c)
if not c:
continue
for n in range(1, 9):
with self.subTest(exponent=-n):
self.assertComplexesAreIdentical(c**-n, 1/(c**n))

# Special cases for complex division.
for x in [+2, -2]:
for y in [+0.0, -0.0]:
c = complex(x, y)
with self.subTest(value=c):
self.assertComplexesAreIdentical(c**-1, complex(1/x, -y))
c = complex(y, x)
with self.subTest(value=c):
self.assertComplexesAreIdentical(c**-1, complex(y, -1/x))
for x in [+INF, -INF]:
for y in [+1, -1]:
c = complex(x, y)
with self.subTest(value=c):
self.assertComplexesAreIdentical(c**-1, complex(1/x, -0.0*y))
self.assertComplexesAreIdentical(c**-2, complex(0.0, -y/x))
c = complex(y, x)
with self.subTest(value=c):
self.assertComplexesAreIdentical(c**-1, complex(+0.0*y, -1/x))
self.assertComplexesAreIdentical(c**-2, complex(-0.0, -y/x))

# Test that zeros have the same sign as small non-zero values.
eps = 1e-11
pairs = [(complex(x, y), complex(x, copysign(0.0, y)))
for x in [+1, -1] for y in [+eps, -eps]]
pairs += [(complex(y, x), complex(copysign(0.0, y), x))
for x in [+1, -1] for y in [+eps, -eps]]
for c1, c2 in pairs:
for n in exponents:
with self.subTest(value=c1, exponent=n):
r1 = c1**n
r2 = c2**n
self.assertClose(r1, r2)
self.assertSameSign(r1.real, r2.real)
self.assertSameSign(r1.imag, r2.imag)
self.assertNotEqual(r1.real, 0.0)
if n != 0:
self.assertNotEqual(r1.imag, 0.0)
self.assertTrue(r2.real == 0.0 or r2.imag == 0.0)

def test_boolcontext(self):
for i in range(100):
self.assertTrue(complex(random() + 1e-6, random() + 1e-6))
Expand Down
Loading
Loading