| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 439c5fe commit d5519ed
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -207,6 +207,38 @@ Classification functions | |||
| 207 | 207 | and ``False`` otherwise. | |
| 208 | 208 | ||
| 209 | 209 | ||
| 210 | + .. function:: isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0) | ||
| 211 | + | ||
| 212 | + Return ``True`` if the values *a* and *b* are close to each other and | ||
| 213 | + ``False`` otherwise. | ||
| 214 | + | ||
| 215 | + Whether or not two values are considered close is determined according to | ||
| 216 | + given absolute and relative tolerances. | ||
| 217 | + | ||
| 218 | + *rel_tol* is the relative tolerance -- it is the maximum allowed difference | ||
| 219 | + between *a* and *b*, relative to the larger absolute value of *a* or *b*. | ||
| 220 | + For example, to set a tolerance of 5%, pass ``rel_tol=0.05``. The default | ||
| 221 | + tolerance is ``1e-09``, which assures that the two values are the same | ||
| 222 | + within about 9 decimal digits. *rel_tol* must be greater than zero. | ||
| 223 | + | ||
| 224 | + *abs_tol* is the minimum absolute tolerance -- useful for comparisons near | ||
| 225 | + zero. *abs_tol* must be at least zero. | ||
| 226 | + | ||
| 227 | + If no errors occur, the result will be: | ||
| 228 | + ``abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)``. | ||
| 229 | + | ||
| 230 | + The IEEE 754 special values of ``NaN``, ``inf``, and ``-inf`` will be | ||
| 231 | + handled according to IEEE rules. Specifically, ``NaN`` is not considered | ||
| 232 | + close to any other value, including ``NaN``. ``inf`` and ``-inf`` are only | ||
| 233 | + considered close to themselves. | ||
| 234 | + | ||
| 235 | + .. versionadded:: 3.5 | ||
| 236 | + | ||
| 237 | + .. seealso:: | ||
| 238 | + | ||
| 239 | + :pep:`485` -- A function for testing approximate equality | ||
| 240 | + | ||
| 241 | + | ||
| 210 | 242 | Constants | |
| 211 | 243 | --------- | |
| 212 | 244 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -110,6 +110,38 @@ Number-theoretic and representation functions | |||
| 110 | 110 | .. versionadded:: 3.5 | |
| 111 | 111 | ||
| 112 | 112 | ||
| 113 | + .. function:: isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0) | ||
| 114 | + | ||
| 115 | + Return ``True`` if the values *a* and *b* are close to each other and | ||
| 116 | + ``False`` otherwise. | ||
| 117 | + | ||
| 118 | + Whether or not two values are considered close is determined according to | ||
| 119 | + given absolute and relative tolerances. | ||
| 120 | + | ||
| 121 | + *rel_tol* is the relative tolerance -- it is the maximum allowed difference | ||
| 122 | + between *a* and *b*, relative to the larger absolute value of *a* or *b*. | ||
| 123 | + For example, to set a tolerance of 5%, pass ``rel_tol=0.05``. The default | ||
| 124 | + tolerance is ``1e-09``, which assures that the two values are the same | ||
| 125 | + within about 9 decimal digits. *rel_tol* must be greater than zero. | ||
| 126 | + | ||
| 127 | + *abs_tol* is the minimum absolute tolerance -- useful for comparisons near | ||
| 128 | + zero. *abs_tol* must be at least zero. | ||
| 129 | + | ||
| 130 | + If no errors occur, the result will be: | ||
| 131 | + ``abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)``. | ||
| 132 | + | ||
| 133 | + The IEEE 754 special values of ``NaN``, ``inf``, and ``-inf`` will be | ||
| 134 | + handled according to IEEE rules. Specifically, ``NaN`` is not considered | ||
| 135 | + close to any other value, including ``NaN``. ``inf`` and ``-inf`` are only | ||
| 136 | + considered close to themselves. | ||
| 137 | + | ||
| 138 | + .. versionadded:: 3.5 | ||
| 139 | + | ||
| 140 | + .. seealso:: | ||
| 141 | + | ||
| 142 | + :pep:`485` -- A function for testing approximate equality | ||
| 143 | + | ||
| 144 | + | ||
| 113 | 145 | .. function:: isfinite(x) | |
| 114 | 146 | ||
| 115 | 147 | Return ``True`` if *x* is neither an infinity nor a NaN, and | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -285,6 +285,18 @@ rather than being restricted to ASCII. | |||
| 285 | 285 | ||
| 286 | 286 | :pep:`488` -- Multi-phase extension module initialization | |
| 287 | 287 | ||
| 288 | + PEP 485: A function for testing approximate equality | ||
| 289 | + ---------------------------------------------------- | ||
| 290 | + | ||
| 291 | + :pep:`485` adds the :func:`math.isclose` and :func:`cmath.isclose` | ||
| 292 | + functions which tell whether two values are approximately equal or | ||
| 293 | + "close" to each other. Whether or not two values are considered | ||
| 294 | + close is determined according to given absolute and relative tolerances. | ||
| 295 | + | ||
| 296 | + .. seealso:: | ||
| 297 | + | ||
| 298 | + :pep:`485` -- A function for testing approximate equality | ||
| 299 | + | ||
| 288 | 300 | Other Language Changes | |
| 289 | 301 | ====================== | |
| 290 | 302 | ||
@@ -346,6 +358,13 @@ cgi | |||
| 346 | 358 | * :class:`~cgi.FieldStorage` now supports the context management protocol. | |
| 347 | 359 | (Contributed by Berker Peksag in :issue:`20289`.) | |
| 348 | 360 | ||
| 361 | + cmath | ||
| 362 | + ----- | ||
| 363 | + | ||
| 364 | + * :func:`cmath.isclose` function added. | ||
| 365 | + (Contributed by Chris Barker and Tal Einat in :issue:`24270`.) | ||
| 366 | + | ||
| 367 | + | ||
| 349 | 368 | code | |
| 350 | 369 | ---- | |
| 351 | 370 | ||
@@ -578,6 +597,8 @@ math | |||
| 578 | 597 | ||
| 579 | 598 | * :data:`math.inf` and :data:`math.nan` constants added. (Contributed by Mark | |
| 580 | 599 | Dickinson in :issue:`23185`.) | |
| 600 | + * :func:`math.isclose` function added. | ||
| 601 | + (Contributed by Chris Barker and Tal Einat in :issue:`24270`.) | ||
| 581 | 602 | ||
| 582 | 603 | shutil | |
| 583 | 604 | ------ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,6 @@ | |||
| 1 | 1 | from test.support import requires_IEEE_754 | |
| 2 | 2 | from test.test_math import parse_testfile, test_file | |
| 3 | + import test.test_math as test_math | ||
| 3 | 4 | import unittest | |
| 4 | 5 | import cmath, math | |
| 5 | 6 | from cmath import phase, polar, rect, pi | |
@@ -529,5 +530,46 @@ def testAtanhSign(self): | |||
| 529 | 530 | self.assertComplexIdentical(cmath.atanh(z), z) | |
| 530 | 531 | ||
| 531 | 532 | ||
| 533 | + class IsCloseTests(test_math.IsCloseTests): | ||
| 534 | + isclose = cmath.isclose | ||
| 535 | + | ||
| 536 | + def test_reject_complex_tolerances(self): | ||
| 537 | + with self.assertRaises(TypeError): | ||
| 538 | + self.isclose(1j, 1j, rel_tol=1j) | ||
| 539 | + | ||
| 540 | + with self.assertRaises(TypeError): | ||
| 541 | + self.isclose(1j, 1j, abs_tol=1j) | ||
| 542 | + | ||
| 543 | + with self.assertRaises(TypeError): | ||
| 544 | + self.isclose(1j, 1j, rel_tol=1j, abs_tol=1j) | ||
| 545 | + | ||
| 546 | + def test_complex_values(self): | ||
| 547 | + # test complex values that are close to within 12 decimal places | ||
| 548 | + complex_examples = [(1.0+1.0j, 1.000000000001+1.0j), | ||
| 549 | + (1.0+1.0j, 1.0+1.000000000001j), | ||
| 550 | + (-1.0+1.0j, -1.000000000001+1.0j), | ||
| 551 | + (1.0-1.0j, 1.0-0.999999999999j), | ||
| 552 | + ] | ||
| 553 | + | ||
| 554 | + self.assertAllClose(complex_examples, rel_tol=1e-12) | ||
| 555 | + self.assertAllNotClose(complex_examples, rel_tol=1e-13) | ||
| 556 | + | ||
| 557 | + def test_complex_near_zero(self): | ||
| 558 | + # test values near zero that are near to within three decimal places | ||
| 559 | + near_zero_examples = [(0.001j, 0), | ||
| 560 | + (0.001, 0), | ||
| 561 | + (0.001+0.001j, 0), | ||
| 562 | + (-0.001+0.001j, 0), | ||
| 563 | + (0.001-0.001j, 0), | ||
| 564 | + (-0.001-0.001j, 0), | ||
| 565 | + ] | ||
| 566 | + | ||
| 567 | + self.assertAllClose(near_zero_examples, abs_tol=1.5e-03) | ||
| 568 | + self.assertAllNotClose(near_zero_examples, abs_tol=0.5e-03) | ||
| 569 | + | ||
| 570 | + self.assertIsClose(0.001-0.001j, 0.001+0.001j, abs_tol=2e-03) | ||
| 571 | + self.assertIsNotClose(0.001-0.001j, 0.001+0.001j, abs_tol=1e-03) | ||
| 572 | + | ||
| 573 | + | ||
| 532 | 574 | if __name__ == "__main__": | |
| 533 | 575 | unittest.main() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1166,10 +1166,131 @@ def test_mtestfile(self): | |||
| 1166 | 1166 | '\n '.join(failures)) | |
| 1167 | 1167 | ||
| 1168 | 1168 | ||
| 1169 | + class IsCloseTests(unittest.TestCase): | ||
| 1170 | + isclose = math.isclose # sublcasses should override this | ||
| 1171 | + | ||
| 1172 | + def assertIsClose(self, a, b, *args, **kwargs): | ||
| 1173 | + self.assertTrue(self.isclose(a, b, *args, **kwargs), | ||
| 1174 | + msg="%s and %s should be close!" % (a, b)) | ||
| 1175 | + | ||
| 1176 | + def assertIsNotClose(self, a, b, *args, **kwargs): | ||
| 1177 | + self.assertFalse(self.isclose(a, b, *args, **kwargs), | ||
| 1178 | + msg="%s and %s should not be close!" % (a, b)) | ||
| 1179 | + | ||
| 1180 | + def assertAllClose(self, examples, *args, **kwargs): | ||
| 1181 | + for a, b in examples: | ||
| 1182 | + self.assertIsClose(a, b, *args, **kwargs) | ||
| 1183 | + | ||
| 1184 | + def assertAllNotClose(self, examples, *args, **kwargs): | ||
| 1185 | + for a, b in examples: | ||
| 1186 | + self.assertIsNotClose(a, b, *args, **kwargs) | ||
| 1187 | + | ||
| 1188 | + def test_negative_tolerances(self): | ||
| 1189 | + # ValueError should be raised if either tolerance is less than zero | ||
| 1190 | + with self.assertRaises(ValueError): | ||
| 1191 | + self.assertIsClose(1, 1, rel_tol=-1e-100) | ||
| 1192 | + with self.assertRaises(ValueError): | ||
| 1193 | + self.assertIsClose(1, 1, rel_tol=1e-100, abs_tol=-1e10) | ||
| 1194 | + | ||
| 1195 | + def test_identical(self): | ||
| 1196 | + # identical values must test as close | ||
| 1197 | + identical_examples = [(2.0, 2.0), | ||
| 1198 | + (0.1e200, 0.1e200), | ||
| 1199 | + (1.123e-300, 1.123e-300), | ||
| 1200 | + (12345, 12345.0), | ||
| 1201 | + (0.0, -0.0), | ||
| 1202 | + (345678, 345678)] | ||
| 1203 | + self.assertAllClose(identical_examples, rel_tol=0.0, abs_tol=0.0) | ||
| 1204 | + | ||
| 1205 | + def test_eight_decimal_places(self): | ||
| 1206 | + # examples that are close to 1e-8, but not 1e-9 | ||
| 1207 | + eight_decimal_places_examples = [(1e8, 1e8 + 1), | ||
| 1208 | + (-1e-8, -1.000000009e-8), | ||
| 1209 | + (1.12345678, 1.12345679)] | ||
| 1210 | + self.assertAllClose(eight_decimal_places_examples, rel_tol=1e-8) | ||
| 1211 | + self.assertAllNotClose(eight_decimal_places_examples, rel_tol=1e-9) | ||
| 1212 | + | ||
| 1213 | + def test_near_zero(self): | ||
| 1214 | + # values close to zero | ||
| 1215 | + near_zero_examples = [(1e-9, 0.0), | ||
| 1216 | + (-1e-9, 0.0), | ||
| 1217 | + (-1e-150, 0.0)] | ||
| 1218 | + # these should not be close to any rel_tol | ||
| 1219 | + self.assertAllNotClose(near_zero_examples, rel_tol=0.9) | ||
| 1220 | + # these should be close to abs_tol=1e-8 | ||
| 1221 | + self.assertAllClose(near_zero_examples, abs_tol=1e-8) | ||
| 1222 | + | ||
| 1223 | + def test_identical_infinite(self): | ||
| 1224 | + # these are close regardless of tolerance -- i.e. they are equal | ||
| 1225 | + self.assertIsClose(INF, INF) | ||
| 1226 | + self.assertIsClose(INF, INF, abs_tol=0.0) | ||
| 1227 | + self.assertIsClose(NINF, NINF) | ||
| 1228 | + self.assertIsClose(NINF, NINF, abs_tol=0.0) | ||
| 1229 | + | ||
| 1230 | + def test_inf_ninf_nan(self): | ||
| 1231 | + # these should never be close (following IEEE 754 rules for equality) | ||
| 1232 | + not_close_examples = [(NAN, NAN), | ||
| 1233 | + (NAN, 1e-100), | ||
| 1234 | + (1e-100, NAN), | ||
| 1235 | + (INF, NAN), | ||
| 1236 | + (NAN, INF), | ||
| 1237 | + (INF, NINF), | ||
| 1238 | + (INF, 1.0), | ||
| 1239 | + (1.0, INF), | ||
| 1240 | + (INF, 1e308), | ||
| 1241 | + (1e308, INF)] | ||
| 1242 | + # use largest reasonable tolerance | ||
| 1243 | + self.assertAllNotClose(not_close_examples, abs_tol=0.999999999999999) | ||
| 1244 | + | ||
| 1245 | + def test_zero_tolerance(self): | ||
| 1246 | + # test with zero tolerance | ||
| 1247 | + zero_tolerance_close_examples = [(1.0, 1.0), | ||
| 1248 | + (-3.4, -3.4), | ||
| 1249 | + (-1e-300, -1e-300)] | ||
| 1250 | + self.assertAllClose(zero_tolerance_close_examples, rel_tol=0.0) | ||
| 1251 | + | ||
| 1252 | + zero_tolerance_not_close_examples = [(1.0, 1.000000000000001), | ||
| 1253 | + (0.99999999999999, 1.0), | ||
| 1254 | + (1.0e200, .999999999999999e200)] | ||
| 1255 | + self.assertAllNotClose(zero_tolerance_not_close_examples, rel_tol=0.0) | ||
| 1256 | + | ||
| 1257 | + def test_assymetry(self): | ||
| 1258 | + # test the assymetry example from PEP 485 | ||
| 1259 | + self.assertAllClose([(9, 10), (10, 9)], rel_tol=0.1) | ||
| 1260 | + | ||
| 1261 | + def test_integers(self): | ||
| 1262 | + # test with integer values | ||
| 1263 | + integer_examples = [(100000001, 100000000), | ||
| 1264 | + (123456789, 123456788)] | ||
| 1265 | + | ||
| 1266 | + self.assertAllClose(integer_examples, rel_tol=1e-8) | ||
| 1267 | + self.assertAllNotClose(integer_examples, rel_tol=1e-9) | ||
| 1268 | + | ||
| 1269 | + def test_decimals(self): | ||
| 1270 | + # test with Decimal values | ||
| 1271 | + from decimal import Decimal | ||
| 1272 | + | ||
| 1273 | + decimal_examples = [(Decimal('1.00000001'), Decimal('1.0')), | ||
| 1274 | + (Decimal('1.00000001e-20'), Decimal('1.0e-20')), | ||
| 1275 | + (Decimal('1.00000001e-100'), Decimal('1.0e-100'))] | ||
| 1276 | + self.assertAllClose(decimal_examples, rel_tol=1e-8) | ||
| 1277 | + self.assertAllNotClose(decimal_examples, rel_tol=1e-9) | ||
| 1278 | + | ||
| 1279 | + def test_fractions(self): | ||
| 1280 | + # test with Fraction values | ||
| 1281 | + from fractions import Fraction | ||
| 1282 | + | ||
| 1283 | + # could use some more examples here! | ||
| 1284 | + fraction_examples = [(Fraction(1, 100000000) + 1, Fraction(1))] | ||
| 1285 | + self.assertAllClose(fraction_examples, rel_tol=1e-8) | ||
| 1286 | + self.assertAllNotClose(fraction_examples, rel_tol=1e-9) | ||
| 1287 | + | ||
| 1288 | + | ||
| 1169 | 1289 | def test_main(): | |
| 1170 | 1290 | from doctest import DocFileSuite | |
| 1171 | 1291 | suite = unittest.TestSuite() | |
| 1172 | 1292 | suite.addTest(unittest.makeSuite(MathTests)) | |
| 1293 | + suite.addTest(unittest.makeSuite(IsCloseTests)) | ||
| 1173 | 1294 | suite.addTest(DocFileSuite("ieee754.txt")) | |
| 1174 | 1295 | run_unittest(suite) | |
| 1175 | 1296 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -273,6 +273,9 @@ Library | |||
| 273 | 273 | - Issue #24298: Fix inspect.signature() to correctly unwrap wrappers | |
| 274 | 274 | around bound methods. | |
| 275 | 275 | ||
| 276 | + - Issue #24270: Add math.isclose() and cmath.isclose() functions as per PEP 485. | ||
| 277 | + Contributed by Chris Barker and Tal Einat. | ||
| 278 | + | ||
| 276 | 279 | IDLE | |
| 277 | 280 | ---- | |
| 278 | 281 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -806,4 +806,55 @@ cmath_isinf(PyModuleDef *module, PyObject *arg) | |||
| 806 | 806 | exit: | |
| 807 | 807 | return return_value; | |
| 808 | 808 | } | |
| 809 | - /*[clinic end generated code: output=274f59792cf4f418 input=a9049054013a1b77]*/ | ||
| 809 | + | ||
| 810 | + PyDoc_STRVAR(cmath_isclose__doc__, | ||
| 811 | + "isclose($module, /, a, b, *, rel_tol=1e-09, abs_tol=0.0)\n" | ||
| 812 | + "--\n" | ||
| 813 | + "\n" | ||
| 814 | + "Determine whether two complex numbers are close in value.\n" | ||
| 815 | + "\n" | ||
| 816 | + " rel_tol\n" | ||
| 817 | + " maximum difference for being considered \"close\", relative to the\n" | ||
| 818 | + " magnitude of the input values\n" | ||
| 819 | + " abs_tol\n" | ||
| 820 | + " maximum difference for being considered \"close\", regardless of the\n" | ||
| 821 | + " magnitude of the input values\n" | ||
| 822 | + "\n" | ||
| 823 | + "Return True if a is close in value to b, and False otherwise.\n" | ||
| 824 | + "\n" | ||
| 825 | + "For the values to be considered close, the difference between them must be\n" | ||
| 826 | + "smaller than at least one of the tolerances.\n" | ||
| 827 | + "\n" | ||
| 828 | + "-inf, inf and NaN behave similarly to the IEEE 754 Standard. That is, NaN is\n" | ||
| 829 | + "not close to anything, even itself. inf and -inf are only close to themselves."); | ||
| 830 | + | ||
| 831 | + #define CMATH_ISCLOSE_METHODDEF \ | ||
| 832 | + {"isclose", (PyCFunction)cmath_isclose, METH_VARARGS|METH_KEYWORDS, cmath_isclose__doc__}, | ||
| 833 | + | ||
| 834 | + static int | ||
| 835 | + cmath_isclose_impl(PyModuleDef *module, Py_complex a, Py_complex b, | ||
| 836 | + double rel_tol, double abs_tol); | ||
| 837 | + | ||
| 838 | + static PyObject * | ||
| 839 | + cmath_isclose(PyModuleDef *module, PyObject *args, PyObject *kwargs) | ||
| 840 | + { | ||
| 841 | + PyObject *return_value = NULL; | ||
| 842 | + static char *_keywords[] = {"a", "b", "rel_tol", "abs_tol", NULL}; | ||
| 843 | + Py_complex a; | ||
| 844 | + Py_complex b; | ||
| 845 | + double rel_tol = 1e-09; | ||
| 846 | + double abs_tol = 0.0; | ||
| 847 | + int _return_value; | ||
| 848 | + | ||
| 849 | + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "DD|$dd:isclose", _keywords, | ||
| 850 | + &a, &b, &rel_tol, &abs_tol)) | ||
| 851 | + goto exit; | ||
| 852 | + _return_value = cmath_isclose_impl(module, a, b, rel_tol, abs_tol); | ||
| 853 | + if ((_return_value == -1) && PyErr_Occurred()) | ||
| 854 | + goto exit; | ||
| 855 | + return_value = PyBool_FromLong((long)_return_value); | ||
| 856 | + | ||
| 857 | + exit: | ||
| 858 | + return return_value; | ||
| 859 | + } | ||
| 860 | + /*[clinic end generated code: output=229e9c48c9d27362 input=a9049054013a1b77]*/ | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments