Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Include/internal/pycore_complexobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,29 @@ PyAPI_FUNC(Py_complex) _Py_cr_prod(Py_complex, double);
PyAPI_FUNC(Py_complex) _Py_cr_quot(Py_complex, double);
PyAPI_FUNC(Py_complex) _Py_rc_quot(double, Py_complex);

static inline bool
_Py_c_isnan(Py_complex a)
{
return isnan(a.real) || isnan(a.imag);
}

static inline bool
_Py_c_isinf(Py_complex a)
{
return isinf(a.real) || isinf(a.imag);
}

static inline bool
_Py_c_isfinite(Py_complex a)
{
return isfinite(a.real) && isfinite(a.imag);
}

static inline bool
_Py_c_iszero(Py_complex a)
{
return a.real == 0.0 && a.imag == 0.0;
}

#ifdef __cplusplus
}
Expand Down
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