Issue #19543: Implementation of isclose as per PEP 485
For details, see: PEP 0485 -- A Function for testing approximate equality Functions added: math.isclose() and cmath.isclose(). Original code by Chris Barker. Patch by Tal Einat.
This commit is contained in:
parent
439c5fe3ae
commit
d5519ed7f4
|
@ -207,6 +207,38 @@ Classification functions
|
||||||
and ``False`` otherwise.
|
and ``False`` otherwise.
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0)
|
||||||
|
|
||||||
|
Return ``True`` if the values *a* and *b* are close to each other and
|
||||||
|
``False`` otherwise.
|
||||||
|
|
||||||
|
Whether or not two values are considered close is determined according to
|
||||||
|
given absolute and relative tolerances.
|
||||||
|
|
||||||
|
*rel_tol* is the relative tolerance -- it is the maximum allowed difference
|
||||||
|
between *a* and *b*, relative to the larger absolute value of *a* or *b*.
|
||||||
|
For example, to set a tolerance of 5%, pass ``rel_tol=0.05``. The default
|
||||||
|
tolerance is ``1e-09``, which assures that the two values are the same
|
||||||
|
within about 9 decimal digits. *rel_tol* must be greater than zero.
|
||||||
|
|
||||||
|
*abs_tol* is the minimum absolute tolerance -- useful for comparisons near
|
||||||
|
zero. *abs_tol* must be at least zero.
|
||||||
|
|
||||||
|
If no errors occur, the result will be:
|
||||||
|
``abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)``.
|
||||||
|
|
||||||
|
The IEEE 754 special values of ``NaN``, ``inf``, and ``-inf`` will be
|
||||||
|
handled according to IEEE rules. Specifically, ``NaN`` is not considered
|
||||||
|
close to any other value, including ``NaN``. ``inf`` and ``-inf`` are only
|
||||||
|
considered close to themselves.
|
||||||
|
|
||||||
|
.. versionadded:: 3.5
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
:pep:`485` -- A function for testing approximate equality
|
||||||
|
|
||||||
|
|
||||||
Constants
|
Constants
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,38 @@ Number-theoretic and representation functions
|
||||||
.. versionadded:: 3.5
|
.. versionadded:: 3.5
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0)
|
||||||
|
|
||||||
|
Return ``True`` if the values *a* and *b* are close to each other and
|
||||||
|
``False`` otherwise.
|
||||||
|
|
||||||
|
Whether or not two values are considered close is determined according to
|
||||||
|
given absolute and relative tolerances.
|
||||||
|
|
||||||
|
*rel_tol* is the relative tolerance -- it is the maximum allowed difference
|
||||||
|
between *a* and *b*, relative to the larger absolute value of *a* or *b*.
|
||||||
|
For example, to set a tolerance of 5%, pass ``rel_tol=0.05``. The default
|
||||||
|
tolerance is ``1e-09``, which assures that the two values are the same
|
||||||
|
within about 9 decimal digits. *rel_tol* must be greater than zero.
|
||||||
|
|
||||||
|
*abs_tol* is the minimum absolute tolerance -- useful for comparisons near
|
||||||
|
zero. *abs_tol* must be at least zero.
|
||||||
|
|
||||||
|
If no errors occur, the result will be:
|
||||||
|
``abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)``.
|
||||||
|
|
||||||
|
The IEEE 754 special values of ``NaN``, ``inf``, and ``-inf`` will be
|
||||||
|
handled according to IEEE rules. Specifically, ``NaN`` is not considered
|
||||||
|
close to any other value, including ``NaN``. ``inf`` and ``-inf`` are only
|
||||||
|
considered close to themselves.
|
||||||
|
|
||||||
|
.. versionadded:: 3.5
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
:pep:`485` -- A function for testing approximate equality
|
||||||
|
|
||||||
|
|
||||||
.. function:: isfinite(x)
|
.. function:: isfinite(x)
|
||||||
|
|
||||||
Return ``True`` if *x* is neither an infinity nor a NaN, and
|
Return ``True`` if *x* is neither an infinity nor a NaN, and
|
||||||
|
|
|
@ -285,6 +285,18 @@ rather than being restricted to ASCII.
|
||||||
|
|
||||||
:pep:`488` -- Multi-phase extension module initialization
|
:pep:`488` -- Multi-phase extension module initialization
|
||||||
|
|
||||||
|
PEP 485: A function for testing approximate equality
|
||||||
|
----------------------------------------------------
|
||||||
|
|
||||||
|
:pep:`485` adds the :func:`math.isclose` and :func:`cmath.isclose`
|
||||||
|
functions which tell whether two values are approximately equal or
|
||||||
|
"close" to each other. Whether or not two values are considered
|
||||||
|
close is determined according to given absolute and relative tolerances.
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
:pep:`485` -- A function for testing approximate equality
|
||||||
|
|
||||||
Other Language Changes
|
Other Language Changes
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
@ -346,6 +358,13 @@ cgi
|
||||||
* :class:`~cgi.FieldStorage` now supports the context management protocol.
|
* :class:`~cgi.FieldStorage` now supports the context management protocol.
|
||||||
(Contributed by Berker Peksag in :issue:`20289`.)
|
(Contributed by Berker Peksag in :issue:`20289`.)
|
||||||
|
|
||||||
|
cmath
|
||||||
|
-----
|
||||||
|
|
||||||
|
* :func:`cmath.isclose` function added.
|
||||||
|
(Contributed by Chris Barker and Tal Einat in :issue:`24270`.)
|
||||||
|
|
||||||
|
|
||||||
code
|
code
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -578,6 +597,8 @@ math
|
||||||
|
|
||||||
* :data:`math.inf` and :data:`math.nan` constants added. (Contributed by Mark
|
* :data:`math.inf` and :data:`math.nan` constants added. (Contributed by Mark
|
||||||
Dickinson in :issue:`23185`.)
|
Dickinson in :issue:`23185`.)
|
||||||
|
* :func:`math.isclose` function added.
|
||||||
|
(Contributed by Chris Barker and Tal Einat in :issue:`24270`.)
|
||||||
|
|
||||||
shutil
|
shutil
|
||||||
------
|
------
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from test.support import requires_IEEE_754
|
from test.support import requires_IEEE_754
|
||||||
from test.test_math import parse_testfile, test_file
|
from test.test_math import parse_testfile, test_file
|
||||||
|
import test.test_math as test_math
|
||||||
import unittest
|
import unittest
|
||||||
import cmath, math
|
import cmath, math
|
||||||
from cmath import phase, polar, rect, pi
|
from cmath import phase, polar, rect, pi
|
||||||
|
@ -529,5 +530,46 @@ class CMathTests(unittest.TestCase):
|
||||||
self.assertComplexIdentical(cmath.atanh(z), z)
|
self.assertComplexIdentical(cmath.atanh(z), z)
|
||||||
|
|
||||||
|
|
||||||
|
class IsCloseTests(test_math.IsCloseTests):
|
||||||
|
isclose = cmath.isclose
|
||||||
|
|
||||||
|
def test_reject_complex_tolerances(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
self.isclose(1j, 1j, rel_tol=1j)
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
self.isclose(1j, 1j, abs_tol=1j)
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
self.isclose(1j, 1j, rel_tol=1j, abs_tol=1j)
|
||||||
|
|
||||||
|
def test_complex_values(self):
|
||||||
|
# test complex values that are close to within 12 decimal places
|
||||||
|
complex_examples = [(1.0+1.0j, 1.000000000001+1.0j),
|
||||||
|
(1.0+1.0j, 1.0+1.000000000001j),
|
||||||
|
(-1.0+1.0j, -1.000000000001+1.0j),
|
||||||
|
(1.0-1.0j, 1.0-0.999999999999j),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertAllClose(complex_examples, rel_tol=1e-12)
|
||||||
|
self.assertAllNotClose(complex_examples, rel_tol=1e-13)
|
||||||
|
|
||||||
|
def test_complex_near_zero(self):
|
||||||
|
# test values near zero that are near to within three decimal places
|
||||||
|
near_zero_examples = [(0.001j, 0),
|
||||||
|
(0.001, 0),
|
||||||
|
(0.001+0.001j, 0),
|
||||||
|
(-0.001+0.001j, 0),
|
||||||
|
(0.001-0.001j, 0),
|
||||||
|
(-0.001-0.001j, 0),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertAllClose(near_zero_examples, abs_tol=1.5e-03)
|
||||||
|
self.assertAllNotClose(near_zero_examples, abs_tol=0.5e-03)
|
||||||
|
|
||||||
|
self.assertIsClose(0.001-0.001j, 0.001+0.001j, abs_tol=2e-03)
|
||||||
|
self.assertIsNotClose(0.001-0.001j, 0.001+0.001j, abs_tol=1e-03)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -1166,10 +1166,131 @@ class MathTests(unittest.TestCase):
|
||||||
'\n '.join(failures))
|
'\n '.join(failures))
|
||||||
|
|
||||||
|
|
||||||
|
class IsCloseTests(unittest.TestCase):
|
||||||
|
isclose = math.isclose # sublcasses should override this
|
||||||
|
|
||||||
|
def assertIsClose(self, a, b, *args, **kwargs):
|
||||||
|
self.assertTrue(self.isclose(a, b, *args, **kwargs),
|
||||||
|
msg="%s and %s should be close!" % (a, b))
|
||||||
|
|
||||||
|
def assertIsNotClose(self, a, b, *args, **kwargs):
|
||||||
|
self.assertFalse(self.isclose(a, b, *args, **kwargs),
|
||||||
|
msg="%s and %s should not be close!" % (a, b))
|
||||||
|
|
||||||
|
def assertAllClose(self, examples, *args, **kwargs):
|
||||||
|
for a, b in examples:
|
||||||
|
self.assertIsClose(a, b, *args, **kwargs)
|
||||||
|
|
||||||
|
def assertAllNotClose(self, examples, *args, **kwargs):
|
||||||
|
for a, b in examples:
|
||||||
|
self.assertIsNotClose(a, b, *args, **kwargs)
|
||||||
|
|
||||||
|
def test_negative_tolerances(self):
|
||||||
|
# ValueError should be raised if either tolerance is less than zero
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.assertIsClose(1, 1, rel_tol=-1e-100)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.assertIsClose(1, 1, rel_tol=1e-100, abs_tol=-1e10)
|
||||||
|
|
||||||
|
def test_identical(self):
|
||||||
|
# identical values must test as close
|
||||||
|
identical_examples = [(2.0, 2.0),
|
||||||
|
(0.1e200, 0.1e200),
|
||||||
|
(1.123e-300, 1.123e-300),
|
||||||
|
(12345, 12345.0),
|
||||||
|
(0.0, -0.0),
|
||||||
|
(345678, 345678)]
|
||||||
|
self.assertAllClose(identical_examples, rel_tol=0.0, abs_tol=0.0)
|
||||||
|
|
||||||
|
def test_eight_decimal_places(self):
|
||||||
|
# examples that are close to 1e-8, but not 1e-9
|
||||||
|
eight_decimal_places_examples = [(1e8, 1e8 + 1),
|
||||||
|
(-1e-8, -1.000000009e-8),
|
||||||
|
(1.12345678, 1.12345679)]
|
||||||
|
self.assertAllClose(eight_decimal_places_examples, rel_tol=1e-8)
|
||||||
|
self.assertAllNotClose(eight_decimal_places_examples, rel_tol=1e-9)
|
||||||
|
|
||||||
|
def test_near_zero(self):
|
||||||
|
# values close to zero
|
||||||
|
near_zero_examples = [(1e-9, 0.0),
|
||||||
|
(-1e-9, 0.0),
|
||||||
|
(-1e-150, 0.0)]
|
||||||
|
# these should not be close to any rel_tol
|
||||||
|
self.assertAllNotClose(near_zero_examples, rel_tol=0.9)
|
||||||
|
# these should be close to abs_tol=1e-8
|
||||||
|
self.assertAllClose(near_zero_examples, abs_tol=1e-8)
|
||||||
|
|
||||||
|
def test_identical_infinite(self):
|
||||||
|
# these are close regardless of tolerance -- i.e. they are equal
|
||||||
|
self.assertIsClose(INF, INF)
|
||||||
|
self.assertIsClose(INF, INF, abs_tol=0.0)
|
||||||
|
self.assertIsClose(NINF, NINF)
|
||||||
|
self.assertIsClose(NINF, NINF, abs_tol=0.0)
|
||||||
|
|
||||||
|
def test_inf_ninf_nan(self):
|
||||||
|
# these should never be close (following IEEE 754 rules for equality)
|
||||||
|
not_close_examples = [(NAN, NAN),
|
||||||
|
(NAN, 1e-100),
|
||||||
|
(1e-100, NAN),
|
||||||
|
(INF, NAN),
|
||||||
|
(NAN, INF),
|
||||||
|
(INF, NINF),
|
||||||
|
(INF, 1.0),
|
||||||
|
(1.0, INF),
|
||||||
|
(INF, 1e308),
|
||||||
|
(1e308, INF)]
|
||||||
|
# use largest reasonable tolerance
|
||||||
|
self.assertAllNotClose(not_close_examples, abs_tol=0.999999999999999)
|
||||||
|
|
||||||
|
def test_zero_tolerance(self):
|
||||||
|
# test with zero tolerance
|
||||||
|
zero_tolerance_close_examples = [(1.0, 1.0),
|
||||||
|
(-3.4, -3.4),
|
||||||
|
(-1e-300, -1e-300)]
|
||||||
|
self.assertAllClose(zero_tolerance_close_examples, rel_tol=0.0)
|
||||||
|
|
||||||
|
zero_tolerance_not_close_examples = [(1.0, 1.000000000000001),
|
||||||
|
(0.99999999999999, 1.0),
|
||||||
|
(1.0e200, .999999999999999e200)]
|
||||||
|
self.assertAllNotClose(zero_tolerance_not_close_examples, rel_tol=0.0)
|
||||||
|
|
||||||
|
def test_assymetry(self):
|
||||||
|
# test the assymetry example from PEP 485
|
||||||
|
self.assertAllClose([(9, 10), (10, 9)], rel_tol=0.1)
|
||||||
|
|
||||||
|
def test_integers(self):
|
||||||
|
# test with integer values
|
||||||
|
integer_examples = [(100000001, 100000000),
|
||||||
|
(123456789, 123456788)]
|
||||||
|
|
||||||
|
self.assertAllClose(integer_examples, rel_tol=1e-8)
|
||||||
|
self.assertAllNotClose(integer_examples, rel_tol=1e-9)
|
||||||
|
|
||||||
|
def test_decimals(self):
|
||||||
|
# test with Decimal values
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
decimal_examples = [(Decimal('1.00000001'), Decimal('1.0')),
|
||||||
|
(Decimal('1.00000001e-20'), Decimal('1.0e-20')),
|
||||||
|
(Decimal('1.00000001e-100'), Decimal('1.0e-100'))]
|
||||||
|
self.assertAllClose(decimal_examples, rel_tol=1e-8)
|
||||||
|
self.assertAllNotClose(decimal_examples, rel_tol=1e-9)
|
||||||
|
|
||||||
|
def test_fractions(self):
|
||||||
|
# test with Fraction values
|
||||||
|
from fractions import Fraction
|
||||||
|
|
||||||
|
# could use some more examples here!
|
||||||
|
fraction_examples = [(Fraction(1, 100000000) + 1, Fraction(1))]
|
||||||
|
self.assertAllClose(fraction_examples, rel_tol=1e-8)
|
||||||
|
self.assertAllNotClose(fraction_examples, rel_tol=1e-9)
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
from doctest import DocFileSuite
|
from doctest import DocFileSuite
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
suite.addTest(unittest.makeSuite(MathTests))
|
suite.addTest(unittest.makeSuite(MathTests))
|
||||||
|
suite.addTest(unittest.makeSuite(IsCloseTests))
|
||||||
suite.addTest(DocFileSuite("ieee754.txt"))
|
suite.addTest(DocFileSuite("ieee754.txt"))
|
||||||
run_unittest(suite)
|
run_unittest(suite)
|
||||||
|
|
||||||
|
|
|
@ -273,6 +273,9 @@ Library
|
||||||
- Issue #24298: Fix inspect.signature() to correctly unwrap wrappers
|
- Issue #24298: Fix inspect.signature() to correctly unwrap wrappers
|
||||||
around bound methods.
|
around bound methods.
|
||||||
|
|
||||||
|
- Issue #24270: Add math.isclose() and cmath.isclose() functions as per PEP 485.
|
||||||
|
Contributed by Chris Barker and Tal Einat.
|
||||||
|
|
||||||
IDLE
|
IDLE
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
|
@ -806,4 +806,55 @@ cmath_isinf(PyModuleDef *module, PyObject *arg)
|
||||||
exit:
|
exit:
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
/*[clinic end generated code: output=274f59792cf4f418 input=a9049054013a1b77]*/
|
|
||||||
|
PyDoc_STRVAR(cmath_isclose__doc__,
|
||||||
|
"isclose($module, /, a, b, *, rel_tol=1e-09, abs_tol=0.0)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Determine whether two complex numbers are close in value.\n"
|
||||||
|
"\n"
|
||||||
|
" rel_tol\n"
|
||||||
|
" maximum difference for being considered \"close\", relative to the\n"
|
||||||
|
" magnitude of the input values\n"
|
||||||
|
" abs_tol\n"
|
||||||
|
" maximum difference for being considered \"close\", regardless of the\n"
|
||||||
|
" magnitude of the input values\n"
|
||||||
|
"\n"
|
||||||
|
"Return True if a is close in value to b, and False otherwise.\n"
|
||||||
|
"\n"
|
||||||
|
"For the values to be considered close, the difference between them must be\n"
|
||||||
|
"smaller than at least one of the tolerances.\n"
|
||||||
|
"\n"
|
||||||
|
"-inf, inf and NaN behave similarly to the IEEE 754 Standard. That is, NaN is\n"
|
||||||
|
"not close to anything, even itself. inf and -inf are only close to themselves.");
|
||||||
|
|
||||||
|
#define CMATH_ISCLOSE_METHODDEF \
|
||||||
|
{"isclose", (PyCFunction)cmath_isclose, METH_VARARGS|METH_KEYWORDS, cmath_isclose__doc__},
|
||||||
|
|
||||||
|
static int
|
||||||
|
cmath_isclose_impl(PyModuleDef *module, Py_complex a, Py_complex b,
|
||||||
|
double rel_tol, double abs_tol);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
cmath_isclose(PyModuleDef *module, PyObject *args, PyObject *kwargs)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
static char *_keywords[] = {"a", "b", "rel_tol", "abs_tol", NULL};
|
||||||
|
Py_complex a;
|
||||||
|
Py_complex b;
|
||||||
|
double rel_tol = 1e-09;
|
||||||
|
double abs_tol = 0.0;
|
||||||
|
int _return_value;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "DD|$dd:isclose", _keywords,
|
||||||
|
&a, &b, &rel_tol, &abs_tol))
|
||||||
|
goto exit;
|
||||||
|
_return_value = cmath_isclose_impl(module, a, b, rel_tol, abs_tol);
|
||||||
|
if ((_return_value == -1) && PyErr_Occurred())
|
||||||
|
goto exit;
|
||||||
|
return_value = PyBool_FromLong((long)_return_value);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
/*[clinic end generated code: output=229e9c48c9d27362 input=a9049054013a1b77]*/
|
||||||
|
|
|
@ -1114,6 +1114,73 @@ cmath_isinf_impl(PyModuleDef *module, Py_complex z)
|
||||||
Py_IS_INFINITY(z.imag));
|
Py_IS_INFINITY(z.imag));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
cmath.isclose -> bool
|
||||||
|
|
||||||
|
a: Py_complex
|
||||||
|
b: Py_complex
|
||||||
|
*
|
||||||
|
rel_tol: double = 1e-09
|
||||||
|
maximum difference for being considered "close", relative to the
|
||||||
|
magnitude of the input values
|
||||||
|
abs_tol: double = 0.0
|
||||||
|
maximum difference for being considered "close", regardless of the
|
||||||
|
magnitude of the input values
|
||||||
|
|
||||||
|
Determine whether two complex numbers are close in value.
|
||||||
|
|
||||||
|
Return True if a is close in value to b, and False otherwise.
|
||||||
|
|
||||||
|
For the values to be considered close, the difference between them must be
|
||||||
|
smaller than at least one of the tolerances.
|
||||||
|
|
||||||
|
-inf, inf and NaN behave similarly to the IEEE 754 Standard. That is, NaN is
|
||||||
|
not close to anything, even itself. inf and -inf are only close to themselves.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
cmath_isclose_impl(PyModuleDef *module, Py_complex a, Py_complex b,
|
||||||
|
double rel_tol, double abs_tol)
|
||||||
|
/*[clinic end generated code: output=da0c535fb54e2310 input=df9636d7de1d4ac3]*/
|
||||||
|
{
|
||||||
|
double diff;
|
||||||
|
|
||||||
|
/* sanity check on the inputs */
|
||||||
|
if (rel_tol < 0.0 || abs_tol < 0.0 ) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"tolerances must be non-negative");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (a.real == b.real) && (a.imag == b.imag) ) {
|
||||||
|
/* short circuit exact equality -- needed to catch two infinities of
|
||||||
|
the same sign. And perhaps speeds things up a bit sometimes.
|
||||||
|
*/
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This catches the case of two infinities of opposite sign, or
|
||||||
|
one infinity and one finite number. Two infinities of opposite
|
||||||
|
sign would otherwise have an infinite relative tolerance.
|
||||||
|
Two infinities of the same sign are caught by the equality check
|
||||||
|
above.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (Py_IS_INFINITY(a.real) || Py_IS_INFINITY(a.imag) ||
|
||||||
|
Py_IS_INFINITY(b.real) || Py_IS_INFINITY(b.imag)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now do the regular computation
|
||||||
|
this is essentially the "weak" test from the Boost library
|
||||||
|
*/
|
||||||
|
|
||||||
|
diff = _Py_c_abs(_Py_c_diff(a, b));
|
||||||
|
|
||||||
|
return (((diff <= rel_tol * _Py_c_abs(b)) ||
|
||||||
|
(diff <= rel_tol * _Py_c_abs(a))) ||
|
||||||
|
(diff <= abs_tol));
|
||||||
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(module_doc,
|
PyDoc_STRVAR(module_doc,
|
||||||
"This module is always available. It provides access to mathematical\n"
|
"This module is always available. It provides access to mathematical\n"
|
||||||
|
@ -1129,6 +1196,7 @@ static PyMethodDef cmath_methods[] = {
|
||||||
CMATH_COS_METHODDEF
|
CMATH_COS_METHODDEF
|
||||||
CMATH_COSH_METHODDEF
|
CMATH_COSH_METHODDEF
|
||||||
CMATH_EXP_METHODDEF
|
CMATH_EXP_METHODDEF
|
||||||
|
CMATH_ISCLOSE_METHODDEF
|
||||||
CMATH_ISFINITE_METHODDEF
|
CMATH_ISFINITE_METHODDEF
|
||||||
CMATH_ISINF_METHODDEF
|
CMATH_ISINF_METHODDEF
|
||||||
CMATH_ISNAN_METHODDEF
|
CMATH_ISNAN_METHODDEF
|
||||||
|
|
|
@ -1990,6 +1990,83 @@ PyDoc_STRVAR(math_isinf_doc,
|
||||||
"isinf(x) -> bool\n\n\
|
"isinf(x) -> bool\n\n\
|
||||||
Return True if x is a positive or negative infinity, and False otherwise.");
|
Return True if x is a positive or negative infinity, and False otherwise.");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
math_isclose(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
|
{
|
||||||
|
double a, b;
|
||||||
|
double rel_tol = 1e-9;
|
||||||
|
double abs_tol = 0.0;
|
||||||
|
double diff = 0.0;
|
||||||
|
long result = 0;
|
||||||
|
|
||||||
|
static char *keywords[] = {"a", "b", "rel_tol", "abs_tol", NULL};
|
||||||
|
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "dd|$dd:isclose",
|
||||||
|
keywords,
|
||||||
|
&a, &b, &rel_tol, &abs_tol
|
||||||
|
))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* sanity check on the inputs */
|
||||||
|
if (rel_tol < 0.0 || abs_tol < 0.0 ) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"tolerances must be non-negative");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( a == b ) {
|
||||||
|
/* short circuit exact equality -- needed to catch two infinities of
|
||||||
|
the same sign. And perhaps speeds things up a bit sometimes.
|
||||||
|
*/
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This catches the case of two infinities of opposite sign, or
|
||||||
|
one infinity and one finite number. Two infinities of opposite
|
||||||
|
sign would otherwise have an infinite relative tolerance.
|
||||||
|
Two infinities of the same sign are caught by the equality check
|
||||||
|
above.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (Py_IS_INFINITY(a) || Py_IS_INFINITY(b)) {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now do the regular computation
|
||||||
|
this is essentially the "weak" test from the Boost library
|
||||||
|
*/
|
||||||
|
|
||||||
|
diff = fabs(b - a);
|
||||||
|
|
||||||
|
result = (((diff <= fabs(rel_tol * b)) ||
|
||||||
|
(diff <= fabs(rel_tol * a))) ||
|
||||||
|
(diff <= abs_tol));
|
||||||
|
|
||||||
|
return PyBool_FromLong(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(math_isclose_doc,
|
||||||
|
"is_close(a, b, *, rel_tol=1e-09, abs_tol=0.0) -> bool\n"
|
||||||
|
"\n"
|
||||||
|
"Determine whether two floating point numbers are close in value.\n"
|
||||||
|
"\n"
|
||||||
|
" rel_tol\n"
|
||||||
|
" maximum difference for being considered \"close\", relative to the\n"
|
||||||
|
" magnitude of the input values\n"
|
||||||
|
" abs_tol\n"
|
||||||
|
" maximum difference for being considered \"close\", regardless of the\n"
|
||||||
|
" magnitude of the input values\n"
|
||||||
|
"\n"
|
||||||
|
"Return True if a is close in value to b, and False otherwise.\n"
|
||||||
|
"\n"
|
||||||
|
"For the values to be considered close, the difference between them\n"
|
||||||
|
"must be smaller than at least one of the tolerances.\n"
|
||||||
|
"\n"
|
||||||
|
"-inf, inf and NaN behave similarly to the IEEE 754 Standard. That\n"
|
||||||
|
"is, NaN is not close to anything, even itself. inf and -inf are\n"
|
||||||
|
"only close to themselves.");
|
||||||
|
|
||||||
static PyMethodDef math_methods[] = {
|
static PyMethodDef math_methods[] = {
|
||||||
{"acos", math_acos, METH_O, math_acos_doc},
|
{"acos", math_acos, METH_O, math_acos_doc},
|
||||||
{"acosh", math_acosh, METH_O, math_acosh_doc},
|
{"acosh", math_acosh, METH_O, math_acosh_doc},
|
||||||
|
@ -2016,6 +2093,8 @@ static PyMethodDef math_methods[] = {
|
||||||
{"gamma", math_gamma, METH_O, math_gamma_doc},
|
{"gamma", math_gamma, METH_O, math_gamma_doc},
|
||||||
{"gcd", math_gcd, METH_VARARGS, math_gcd_doc},
|
{"gcd", math_gcd, METH_VARARGS, math_gcd_doc},
|
||||||
{"hypot", math_hypot, METH_VARARGS, math_hypot_doc},
|
{"hypot", math_hypot, METH_VARARGS, math_hypot_doc},
|
||||||
|
{"isclose", (PyCFunction) math_isclose, METH_VARARGS | METH_KEYWORDS,
|
||||||
|
math_isclose_doc},
|
||||||
{"isfinite", math_isfinite, METH_O, math_isfinite_doc},
|
{"isfinite", math_isfinite, METH_O, math_isfinite_doc},
|
||||||
{"isinf", math_isinf, METH_O, math_isinf_doc},
|
{"isinf", math_isinf, METH_O, math_isinf_doc},
|
||||||
{"isnan", math_isnan, METH_O, math_isnan_doc},
|
{"isnan", math_isnan, METH_O, math_isnan_doc},
|
||||||
|
|
Loading…
Reference in New Issue