| # Python test set -- math module | |
| # XXXX Should not do tests around zero only | |
| from test.support import (verbose, requires_IEEE_754, | |
| skip_if_double_rounding) | |
| from test import support | |
| import unittest | |
| import fractions | |
| import itertools | |
| import decimal | |
| import math | |
| import os | |
| import platform | |
| import random | |
| import struct | |
| import sys | |
| eps = 1E-05 | |
| NAN = float('nan') | |
| NNAN = float('-nan') | |
| INF = float('inf') | |
| NINF = float('-inf') | |
| FLOAT_MAX = sys.float_info.max | |
| FLOAT_MIN = sys.float_info.min | |
| # locate file with test values | |
| if __name__ == '__main__': | |
| file = sys.argv[0] | |
| else: | |
| file = __file__ | |
| test_dir = os.path.dirname(file) or os.curdir | |
| math_testcases = os.path.join(test_dir, 'mathdata', 'math_testcases.txt') | |
| test_file = os.path.join(test_dir, 'mathdata', 'cmath_testcases.txt') | |
| def to_ulps(x): | |
| """Convert a non-NaN float x to an integer, in such a way that | |
| adjacent floats are converted to adjacent integers. Then | |
| abs(ulps(x) - ulps(y)) gives the difference in ulps between two | |
| floats. | |
| The results from this function will only make sense on platforms | |
| where native doubles are represented in IEEE 754 binary64 format. | |
| Note: 0.0 and -0.0 are converted to 0 and -1, respectively. | |
| """ | |
| n = struct.unpack('<q', struct.pack('<d', x))[0] | |
| if n < 0: | |
| n = ~(n+2**63) | |
| return n | |
| def ulp_abs_check(expected, got, ulp_tol, abs_tol): | |
| """Given finite floats `expected` and `got`, check that they're | |
| approximately equal to within the given number of ulps or the | |
| given absolute tolerance, whichever is bigger. | |
| Returns None on success and an error message on failure. | |
| """ | |
| ulp_error = abs(to_ulps(expected) - to_ulps(got)) | |
| abs_error = abs(expected - got) | |
| # Succeed if either abs_error <= abs_tol or ulp_error <= ulp_tol. | |
| if abs_error <= abs_tol or ulp_error <= ulp_tol: | |
| return None | |
| else: | |
| fmt = ("error = {:.3g} ({:d} ulps); " | |
| "permitted error = {:.3g} or {:d} ulps") | |
| return fmt.format(abs_error, ulp_error, abs_tol, ulp_tol) | |
| def parse_mtestfile(fname): | |
| """Parse a file with test values | |
| -- starts a comment | |
| blank lines, or lines containing only a comment, are ignored | |
| other lines are expected to have the form | |
| id fn arg... -> expected [flag]* | |
| """ | |
| with open(fname, encoding="utf-8") as fp: | |
| for line in fp: | |
| # strip comments, and skip blank lines | |
| if '--' in line: | |
| line = line[:line.index('--')] | |
| if not line.strip(): | |
| continue | |
| lhs, rhs = line.split('->') | |
| id, fn, *args = lhs.split() | |
| rhs_pieces = rhs.split() | |
| exp = rhs_pieces[0] | |
| flags = rhs_pieces[1:] | |
| yield (id, fn, [float(arg) for arg in args], float(exp), flags) | |
| def parse_testfile(fname): | |
| """Parse a file with test values | |
| Empty lines or lines starting with -- are ignored | |
| yields id, fn, arg_real, arg_imag, exp_real, exp_imag | |
| """ | |
| with open(fname, encoding="utf-8") as fp: | |
| for line in fp: | |
| # skip comment lines and blank lines | |
| if line.startswith('--') or not line.strip(): | |
| continue | |
| lhs, rhs = line.split('->') | |
| id, fn, arg_real, arg_imag = lhs.split() | |
| rhs_pieces = rhs.split() | |
| exp_real, exp_imag = rhs_pieces[0], rhs_pieces[1] | |
| flags = rhs_pieces[2:] | |
| yield (id, fn, | |
| float(arg_real), float(arg_imag), | |
| float(exp_real), float(exp_imag), | |
| flags) | |
| def result_check(expected, got, ulp_tol=5, abs_tol=0.0): | |
| # Common logic of MathTests.(ftest, test_testcases, test_mtestcases) | |
| """Compare arguments expected and got, as floats, if either | |
| is a float, using a tolerance expressed in multiples of | |
| ulp(expected) or absolutely (if given and greater). | |
| As a convenience, when neither argument is a float, and for | |
| non-finite floats, exact equality is demanded. Also, nan==nan | |
| as far as this function is concerned. | |
| Returns None on success and an error message on failure. | |
| """ | |
| # Check exactly equal (applies also to strings representing exceptions) | |
| if got == expected: | |
| if not got and not expected: | |
| if math.copysign(1, got) != math.copysign(1, expected): | |
| return f"expected {expected}, got {got} (zero has wrong sign)" | |
| return None | |
| failure = "not equal" | |
| # Turn mixed float and int comparison (e.g. floor()) to all-float | |
| if isinstance(expected, float) and isinstance(got, int): | |
| got = float(got) | |
| elif isinstance(got, float) and isinstance(expected, int): | |
| expected = float(expected) | |
| if isinstance(expected, float) and isinstance(got, float): | |
| if math.isnan(expected) and math.isnan(got): | |
| # Pass, since both nan | |
| failure = None | |
| elif math.isinf(expected) or math.isinf(got): | |
| # We already know they're not equal, drop through to failure | |
| pass | |
| else: | |
| # Both are finite floats (now). Are they close enough? | |
| failure = ulp_abs_check(expected, got, ulp_tol, abs_tol) | |
| # arguments are not equal, and if numeric, are too far apart | |
| if failure is not None: | |
| fail_fmt = "expected {!r}, got {!r}" | |
| fail_msg = fail_fmt.format(expected, got) | |
| fail_msg += ' ({})'.format(failure) | |
| return fail_msg | |
| else: | |
| return None | |
| class FloatLike: | |
| def __init__(self, value): | |
| self.value = value | |
| def __float__(self): | |
| return self.value | |
| class IntSubclass(int): | |
| pass | |
| # Class providing an __index__ method. | |
| class MyIndexable(object): | |
| def __init__(self, value): | |
| self.value = value | |
| def __index__(self): | |
| return self.value | |
| class IndexableFloatLike: | |
| def __init__(self, float_value, index_value): | |
| self.float_value = float_value | |
| self.index_value = index_value | |
| def __float__(self): | |
| if isinstance(self.float_value, BaseException): | |
| raise self.float_value | |
| return self.float_value | |
| def __index__(self): | |
| if isinstance(self.index_value, BaseException): | |
| raise self.index_value | |
| return self.index_value | |
| class BadDescr: | |
| def __get__(self, obj, objtype=None): | |
| raise ValueError | |
| class MathTests(unittest.TestCase): | |
| def ftest(self, name, got, expected, ulp_tol=5, abs_tol=0.0): | |
| """Compare arguments expected and got, as floats, if either | |
| is a float, using a tolerance expressed in multiples of | |
| ulp(expected) or absolutely, whichever is greater. | |
| As a convenience, when neither argument is a float, and for | |
| non-finite floats, exact equality is demanded. Also, nan==nan | |
| in this function. | |
| """ | |
| failure = result_check(expected, got, ulp_tol, abs_tol) | |
| if failure is not None: | |
| self.fail("{}: {}".format(name, failure)) | |
| def testConstants(self): | |
| # Ref: Abramowitz & Stegun (Dover, 1965) | |
| self.ftest('pi', math.pi, 3.141592653589793238462643) | |
| self.ftest('e', math.e, 2.718281828459045235360287) | |
| self.assertEqual(math.tau, 2*math.pi) | |
| def testAcos(self): | |
| self.assertRaises(TypeError, math.acos) | |
| self.ftest('acos(-1)', math.acos(-1), math.pi) | |
| self.ftest('acos(0)', math.acos(0), math.pi/2) | |
| self.ftest('acos(1)', math.acos(1), 0) | |
| self.assertRaises(ValueError, math.acos, INF) | |
| self.assertRaises(ValueError, math.acos, NINF) | |
| self.assertRaises(ValueError, math.acos, 1 + eps) | |
| self.assertRaises(ValueError, math.acos, -1 - eps) | |
| self.assertTrue(math.isnan(math.acos(NAN))) | |
| def testAcosh(self): | |
| self.assertRaises(TypeError, math.acosh) | |
| self.ftest('acosh(1)', math.acosh(1), 0) | |
| self.ftest('acosh(2)', math.acosh(2), 1.3169578969248168) | |
| self.assertRaises(ValueError, math.acosh, 0) | |
| self.assertRaises(ValueError, math.acosh, -1) | |
| self.assertEqual(math.acosh(INF), INF) | |
| self.assertRaises(ValueError, math.acosh, NINF) | |
| self.assertTrue(math.isnan(math.acosh(NAN))) | |
| def testAsin(self): | |
| self.assertRaises(TypeError, math.asin) | |
| self.ftest('asin(-1)', math.asin(-1), -math.pi/2) | |
| self.ftest('asin(0)', math.asin(0), 0) | |
| self.ftest('asin(1)', math.asin(1), math.pi/2) | |
| self.assertRaises(ValueError, math.asin, INF) | |
| self.assertRaises(ValueError, math.asin, NINF) | |
| self.assertRaises(ValueError, math.asin, 1 + eps) | |
| self.assertRaises(ValueError, math.asin, -1 - eps) | |
| self.assertTrue(math.isnan(math.asin(NAN))) | |
| def testAsinh(self): | |
| self.assertRaises(TypeError, math.asinh) | |
| self.ftest('asinh(0)', math.asinh(0), 0) | |
| self.ftest('asinh(1)', math.asinh(1), 0.88137358701954305) | |
| self.ftest('asinh(-1)', math.asinh(-1), -0.88137358701954305) | |
| self.assertEqual(math.asinh(INF), INF) | |
| self.assertEqual(math.asinh(NINF), NINF) | |
| self.assertTrue(math.isnan(math.asinh(NAN))) | |
| def testAtan(self): | |
| self.assertRaises(TypeError, math.atan) | |
| self.ftest('atan(-1)', math.atan(-1), -math.pi/4) | |
| self.ftest('atan(0)', math.atan(0), 0) | |
| self.ftest('atan(1)', math.atan(1), math.pi/4) | |
| self.ftest('atan(inf)', math.atan(INF), math.pi/2) | |
| self.ftest('atan(-inf)', math.atan(NINF), -math.pi/2) | |
| self.assertTrue(math.isnan(math.atan(NAN))) | |
| def testAtanh(self): | |
| self.assertRaises(TypeError, math.atan) | |
| self.ftest('atanh(0)', math.atanh(0), 0) | |
| self.ftest('atanh(0.5)', math.atanh(0.5), 0.54930614433405489) | |
| self.ftest('atanh(-0.5)', math.atanh(-0.5), -0.54930614433405489) | |
| self.assertRaises(ValueError, math.atanh, 1) | |
| self.assertRaises(ValueError, math.atanh, -1) | |
| self.assertRaises(ValueError, math.atanh, INF) | |
| self.assertRaises(ValueError, math.atanh, NINF) | |
| self.assertTrue(math.isnan(math.atanh(NAN))) | |
| @unittest.skipIf(sys.platform.startswith("sunos"), | |
| "skipping, see gh-138573") | |
| def testAtan2(self): | |
| self.assertRaises(TypeError, math.atan2) | |
| self.ftest('atan2(-1, 0)', math.atan2(-1, 0), -math.pi/2) | |
| self.ftest('atan2(-1, 1)', math.atan2(-1, 1), -math.pi/4) | |
| self.ftest('atan2(0, 1)', math.atan2(0, 1), 0) | |
| self.ftest('atan2(1, 1)', math.atan2(1, 1), math.pi/4) | |
| self.ftest('atan2(1, 0)', math.atan2(1, 0), math.pi/2) | |
| self.ftest('atan2(1, -1)', math.atan2(1, -1), 3*math.pi/4) | |
| # math.atan2(0, x) | |
| self.ftest('atan2(0., -inf)', math.atan2(0., NINF), math.pi) | |
| self.ftest('atan2(0., -2.3)', math.atan2(0., -2.3), math.pi) | |
| self.ftest('atan2(0., -0.)', math.atan2(0., -0.), math.pi) | |
| self.assertEqual(math.atan2(0., 0.), 0.) | |
| self.assertEqual(math.atan2(0., 2.3), 0.) | |
| self.assertEqual(math.atan2(0., INF), 0.) | |
| self.assertTrue(math.isnan(math.atan2(0., NAN))) | |
| # math.atan2(-0, x) | |
| self.ftest('atan2(-0., -inf)', math.atan2(-0., NINF), -math.pi) | |
| self.ftest('atan2(-0., -2.3)', math.atan2(-0., -2.3), -math.pi) | |
| self.ftest('atan2(-0., -0.)', math.atan2(-0., -0.), -math.pi) | |
| self.assertEqual(math.atan2(-0., 0.), -0.) | |
| self.assertEqual(math.atan2(-0., 2.3), -0.) | |
| self.assertEqual(math.atan2(-0., INF), -0.) | |
| self.assertTrue(math.isnan(math.atan2(-0., NAN))) | |
| # math.atan2(INF, x) | |
| self.ftest('atan2(inf, -inf)', math.atan2(INF, NINF), math.pi*3/4) | |
| self.ftest('atan2(inf, -2.3)', math.atan2(INF, -2.3), math.pi/2) | |
| self.ftest('atan2(inf, -0.)', math.atan2(INF, -0.0), math.pi/2) | |
| self.ftest('atan2(inf, 0.)', math.atan2(INF, 0.0), math.pi/2) | |
| self.ftest('atan2(inf, 2.3)', math.atan2(INF, 2.3), math.pi/2) | |
| self.ftest('atan2(inf, inf)', math.atan2(INF, INF), math.pi/4) | |
| self.assertTrue(math.isnan(math.atan2(INF, NAN))) | |
| # math.atan2(NINF, x) | |
| self.ftest('atan2(-inf, -inf)', math.atan2(NINF, NINF), -math.pi*3/4) | |
| self.ftest('atan2(-inf, -2.3)', math.atan2(NINF, -2.3), -math.pi/2) | |
| self.ftest('atan2(-inf, -0.)', math.atan2(NINF, -0.0), -math.pi/2) | |
| self.ftest('atan2(-inf, 0.)', math.atan2(NINF, 0.0), -math.pi/2) | |
| self.ftest('atan2(-inf, 2.3)', math.atan2(NINF, 2.3), -math.pi/2) | |
| self.ftest('atan2(-inf, inf)', math.atan2(NINF, INF), -math.pi/4) | |
| self.assertTrue(math.isnan(math.atan2(NINF, NAN))) | |
| # math.atan2(+finite, x) | |
| self.ftest('atan2(2.3, -inf)', math.atan2(2.3, NINF), math.pi) | |
| self.ftest('atan2(2.3, -0.)', math.atan2(2.3, -0.), math.pi/2) | |
| self.ftest('atan2(2.3, 0.)', math.atan2(2.3, 0.), math.pi/2) | |
| self.assertEqual(math.atan2(2.3, INF), 0.) | |
| self.assertTrue(math.isnan(math.atan2(2.3, NAN))) | |
| # math.atan2(-finite, x) | |
| self.ftest('atan2(-2.3, -inf)', math.atan2(-2.3, NINF), -math.pi) | |
| self.ftest('atan2(-2.3, -0.)', math.atan2(-2.3, -0.), -math.pi/2) | |
| self.ftest('atan2(-2.3, 0.)', math.atan2(-2.3, 0.), -math.pi/2) | |
| self.assertEqual(math.atan2(-2.3, INF), -0.) | |
| self.assertTrue(math.isnan(math.atan2(-2.3, NAN))) | |
| # math.atan2(NAN, x) | |
| self.assertTrue(math.isnan(math.atan2(NAN, NINF))) | |
| self.assertTrue(math.isnan(math.atan2(NAN, -2.3))) | |
| self.assertTrue(math.isnan(math.atan2(NAN, -0.))) | |
| self.assertTrue(math.isnan(math.atan2(NAN, 0.))) | |
| self.assertTrue(math.isnan(math.atan2(NAN, 2.3))) | |
| self.assertTrue(math.isnan(math.atan2(NAN, INF))) | |
| self.assertTrue(math.isnan(math.atan2(NAN, NAN))) | |
| def testCbrt(self): | |
| self.assertRaises(TypeError, math.cbrt) | |
| self.ftest('cbrt(0)', math.cbrt(0), 0) | |
| self.ftest('cbrt(1)', math.cbrt(1), 1) | |
| self.ftest('cbrt(8)', math.cbrt(8), 2) | |
| self.ftest('cbrt(0.0)', math.cbrt(0.0), 0.0) | |
| self.ftest('cbrt(-0.0)', math.cbrt(-0.0), -0.0) | |
| self.ftest('cbrt(1.2)', math.cbrt(1.2), 1.062658569182611) | |
| self.ftest('cbrt(-2.6)', math.cbrt(-2.6), -1.375068867074141) | |
| self.ftest('cbrt(27)', math.cbrt(27), 3) | |
| self.ftest('cbrt(-1)', math.cbrt(-1), -1) | |
| self.ftest('cbrt(-27)', math.cbrt(-27), -3) | |
| self.assertEqual(math.cbrt(INF), INF) | |
| self.assertEqual(math.cbrt(NINF), NINF) | |
| self.assertTrue(math.isnan(math.cbrt(NAN))) | |
| def testCeil(self): | |
| self.assertRaises(TypeError, math.ceil) | |
| self.assertEqual(int, type(math.ceil(0.5))) | |
| self.assertEqual(math.ceil(0.5), 1) | |
| self.assertEqual(math.ceil(1.0), 1) | |
| self.assertEqual(math.ceil(1.5), 2) | |
| self.assertEqual(math.ceil(-0.5), 0) | |
| self.assertEqual(math.ceil(-1.0), -1) | |
| self.assertEqual(math.ceil(-1.5), -1) | |
| self.assertEqual(math.ceil(0.0), 0) | |
| self.assertEqual(math.ceil(-0.0), 0) | |
| #self.assertEqual(math.ceil(INF), INF) | |
| #self.assertEqual(math.ceil(NINF), NINF) | |
| #self.assertTrue(math.isnan(math.ceil(NAN))) | |
| class TestCeil: | |
| def __ceil__(self): | |
| return 42 | |
| class FloatCeil(float): | |
| def __ceil__(self): | |
| return 42 | |
| class TestNoCeil: | |
| pass | |
| class TestBadCeil: | |
| __ceil__ = BadDescr() | |
| self.assertEqual(math.ceil(TestCeil()), 42) | |
| self.assertEqual(math.ceil(FloatCeil()), 42) | |
| self.assertEqual(math.ceil(FloatLike(42.5)), 43) | |
| self.assertRaises(TypeError, math.ceil, TestNoCeil()) | |
| self.assertRaises(ValueError, math.ceil, TestBadCeil()) | |
| t = TestNoCeil() | |
| t.__ceil__ = lambda *args: args | |
| self.assertRaises(TypeError, math.ceil, t) | |
| self.assertRaises(TypeError, math.ceil, t, 0) | |
| self.assertEqual(math.ceil(FloatLike(+1.0)), +1.0) | |
| self.assertEqual(math.ceil(FloatLike(-1.0)), -1.0) | |
| @requires_IEEE_754 | |
| def testCopysign(self): | |
| self.assertEqual(math.copysign(1, 42), 1.0) | |
| self.assertEqual(math.copysign(0., 42), 0.0) | |
| self.assertEqual(math.copysign(1., -42), -1.0) | |
| self.assertEqual(math.copysign(3, 0.), 3.0) | |
| self.assertEqual(math.copysign(4., -0.), -4.0) | |
| self.assertRaises(TypeError, math.copysign) | |
| # copysign should let us distinguish signs of zeros | |
| self.assertEqual(math.copysign(1., 0.), 1.) | |
| self.assertEqual(math.copysign(1., -0.), -1.) | |
| self.assertEqual(math.copysign(INF, 0.), INF) | |
| self.assertEqual(math.copysign(INF, -0.), NINF) | |
| self.assertEqual(math.copysign(NINF, 0.), INF) | |
| self.assertEqual(math.copysign(NINF, -0.), NINF) | |
| # and of infinities | |
| self.assertEqual(math.copysign(1., INF), 1.) | |
| self.assertEqual(math.copysign(1., NINF), -1.) | |
| self.assertEqual(math.copysign(INF, INF), INF) | |
| self.assertEqual(math.copysign(INF, NINF), NINF) | |
| self.assertEqual(math.copysign(NINF, INF), INF) | |
| self.assertEqual(math.copysign(NINF, NINF), NINF) | |
| self.assertTrue(math.isnan(math.copysign(NAN, 1.))) | |
| self.assertTrue(math.isnan(math.copysign(NAN, INF))) | |
| self.assertTrue(math.isnan(math.copysign(NAN, NINF))) | |
| self.assertTrue(math.isnan(math.copysign(NAN, NAN))) | |
| # copysign(INF, NAN) may be INF or it may be NINF, since | |
| # we don't know whether the sign bit of NAN is set on any | |
| # given platform. | |
| self.assertTrue(math.isinf(math.copysign(INF, NAN))) | |
| # similarly, copysign(2., NAN) could be 2. or -2. | |
| self.assertEqual(abs(math.copysign(2., NAN)), 2.) | |
| def test_signbit(self): | |
| self.assertRaises(TypeError, math.signbit) | |
| self.assertRaises(TypeError, math.signbit, '1.0') | |
| # C11, §7.12.3.6 requires signbit() to return a nonzero value | |
| # if and only if the sign of its argument value is negative, | |
| # but in practice, we are only interested in a boolean value. | |
| self.assertIsInstance(math.signbit(1.0), bool) | |
| for arg in [0., 1., INF, NAN]: | |
| self.assertFalse(math.signbit(arg)) | |
| self.assertTrue(math.signbit(-arg)) | |
| def testCos(self): | |
| self.assertRaises(TypeError, math.cos) | |
| self.ftest('cos(-pi/2)', math.cos(-math.pi/2), 0, abs_tol=math.ulp(1)) | |
| self.ftest('cos(0)', math.cos(0), 1) | |
| self.ftest('cos(pi/2)', math.cos(math.pi/2), 0, abs_tol=math.ulp(1)) | |
| self.ftest('cos(pi)', math.cos(math.pi), -1) | |
| try: | |
| self.assertTrue(math.isnan(math.cos(INF))) | |
| self.assertTrue(math.isnan(math.cos(NINF))) | |
| except ValueError: | |
| self.assertRaises(ValueError, math.cos, INF) | |
| self.assertRaises(ValueError, math.cos, NINF) | |
| self.assertTrue(math.isnan(math.cos(NAN))) | |
| @unittest.skipIf(sys.platform == 'win32' and platform.machine() in ('ARM', 'ARM64'), | |
| "Windows UCRT is off by 2 ULP this test requires accuracy within 1 ULP") | |
| def testCosh(self): | |
| self.assertRaises(TypeError, math.cosh) | |
| self.ftest('cosh(0)', math.cosh(0), 1) | |
| self.ftest('cosh(2)-2*cosh(1)**2', math.cosh(2)-2*math.cosh(1)**2, -1) # Thanks to Lambert | |
| self.assertEqual(math.cosh(INF), INF) | |
| self.assertEqual(math.cosh(NINF), INF) | |
| self.assertTrue(math.isnan(math.cosh(NAN))) | |
| def testDegrees(self): | |
| self.assertRaises(TypeError, math.degrees) | |
| self.ftest('degrees(pi)', math.degrees(math.pi), 180.0) | |
| self.ftest('degrees(pi/2)', math.degrees(math.pi/2), 90.0) | |
| self.ftest('degrees(-pi/4)', math.degrees(-math.pi/4), -45.0) | |
| self.ftest('degrees(0)', math.degrees(0), 0) | |
| def testExp(self): | |
| self.assertRaises(TypeError, math.exp) | |
| self.ftest('exp(-1)', math.exp(-1), 1/math.e) | |
| self.ftest('exp(0)', math.exp(0), 1) | |
| self.ftest('exp(1)', math.exp(1), math.e) | |
| self.assertEqual(math.exp(INF), INF) | |
| self.assertEqual(math.exp(NINF), 0.) | |
| self.assertTrue(math.isnan(math.exp(NAN))) | |
| self.assertRaises(OverflowError, math.exp, 1000000) | |
| def testExp2(self): | |
| self.assertRaises(TypeError, math.exp2) | |
| self.ftest('exp2(-1)', math.exp2(-1), 0.5) | |
| self.ftest('exp2(0)', math.exp2(0), 1) | |
| self.ftest('exp2(1)', math.exp2(1), 2) | |
| self.ftest('exp2(2.3)', math.exp2(2.3), 4.924577653379665) | |
| self.assertEqual(math.exp2(INF), INF) | |
| self.assertEqual(math.exp2(NINF), 0.) | |
| self.assertTrue(math.isnan(math.exp2(NAN))) | |
| self.assertRaises(OverflowError, math.exp2, 1000000) | |
| def testFabs(self): | |
| self.assertRaises(TypeError, math.fabs) | |
| self.ftest('fabs(-1)', math.fabs(-1), 1) | |
| self.ftest('fabs(0)', math.fabs(0), 0) | |
| self.ftest('fabs(1)', math.fabs(1), 1) | |
| def testFloor(self): | |
| self.assertRaises(TypeError, math.floor) | |
| self.assertEqual(int, type(math.floor(0.5))) | |
| self.assertEqual(math.floor(0.5), 0) | |
| self.assertEqual(math.floor(1.0), 1) | |
| self.assertEqual(math.floor(1.5), 1) | |
| self.assertEqual(math.floor(-0.5), -1) | |
| self.assertEqual(math.floor(-1.0), -1) | |
| self.assertEqual(math.floor(-1.5), -2) | |
| #self.assertEqual(math.ceil(INF), INF) | |
| #self.assertEqual(math.ceil(NINF), NINF) | |
| #self.assertTrue(math.isnan(math.floor(NAN))) | |
| class TestFloorIsNone(float): | |
| __floor__ = None | |
| class TestFloor: | |
| def __floor__(self): | |
| return 42 | |
| class FloatFloor(float): | |
| def __floor__(self): | |
| return 42 | |
| class TestNoFloor: | |
| pass | |
| class TestBadFloor: | |
| __floor__ = BadDescr() | |
| self.assertEqual(math.floor(TestFloor()), 42) | |
| self.assertEqual(math.floor(FloatFloor()), 42) | |
| self.assertEqual(math.floor(FloatLike(41.9)), 41) | |
| self.assertRaises(TypeError, math.floor, TestNoFloor()) | |
| self.assertRaises(ValueError, math.floor, TestBadFloor()) | |
| self.assertRaises(TypeError, math.floor, TestFloorIsNone(3.5)) | |
| t = TestNoFloor() | |
| t.__floor__ = lambda *args: args | |
| self.assertRaises(TypeError, math.floor, t) | |
| self.assertRaises(TypeError, math.floor, t, 0) | |
| self.assertEqual(math.floor(FloatLike(+1.0)), +1.0) | |
| self.assertEqual(math.floor(FloatLike(-1.0)), -1.0) | |
| def testFmod(self): | |
| self.assertRaises(TypeError, math.fmod) | |
| self.ftest('fmod(10, 1)', math.fmod(10, 1), 0.0) | |
| self.ftest('fmod(10, 0.5)', math.fmod(10, 0.5), 0.0) | |
| self.ftest('fmod(10, 1.5)', math.fmod(10, 1.5), 1.0) | |
| self.ftest('fmod(-10, 1)', math.fmod(-10, 1), -0.0) | |
| self.ftest('fmod(-10, 0.5)', math.fmod(-10, 0.5), -0.0) | |
| self.ftest('fmod(-10, 1.5)', math.fmod(-10, 1.5), -1.0) | |
| self.assertTrue(math.isnan(math.fmod(NAN, 1.))) | |
| self.assertTrue(math.isnan(math.fmod(1., NAN))) | |
| self.assertTrue(math.isnan(math.fmod(NAN, NAN))) | |
| self.assertRaises(ValueError, math.fmod, 1., 0.) | |
| self.assertRaises(ValueError, math.fmod, INF, 1.) | |
| self.assertRaises(ValueError, math.fmod, NINF, 1.) | |
| self.assertRaises(ValueError, math.fmod, INF, 0.) | |
| self.assertEqual(math.fmod(3.0, INF), 3.0) | |
| self.assertEqual(math.fmod(-3.0, INF), -3.0) | |
| self.assertEqual(math.fmod(3.0, NINF), 3.0) | |
| self.assertEqual(math.fmod(-3.0, NINF), -3.0) | |
| self.assertEqual(math.fmod(0.0, 3.0), 0.0) | |
| self.assertEqual(math.fmod(0.0, NINF), 0.0) | |
| self.assertRaises(ValueError, math.fmod, INF, INF) | |
| def test_fmax(self): | |
| self.assertRaises(TypeError, math.fmax) | |
| self.assertRaises(TypeError, math.fmax, 'x', 'y') | |
| self.assertEqual(math.fmax(0., 0.), 0.) | |
| self.assertEqual(math.fmax(1., 2.), 2.) | |
| self.assertEqual(math.fmax(2., 1.), 2.) | |
| self.assertEqual(math.fmax(+1., +0.), 1.) | |
| self.assertEqual(math.fmax(+0., +1.), 1.) | |
| self.assertEqual(math.fmax(+1., -0.), 1.) | |
| self.assertEqual(math.fmax(-0., +1.), 1.) | |
| self.assertEqual(math.fmax(-1., +0.), 0.) | |
| self.assertEqual(math.fmax(+0., -1.), 0.) | |
| self.assertEqual(math.fmax(-1., -0.), 0.) | |
| self.assertEqual(math.fmax(-0., -1.), 0.) | |
| for x in [NINF, -1., -0., 0., 1., INF]: | |
| self.assertFalse(math.isnan(x)) | |
| with self.subTest(x=x, is_negative=math.copysign(1, x) < 0): | |
| self.assertEqual(math.fmax(INF, x), INF) | |
| self.assertEqual(math.fmax(x, INF), INF) | |
| self.assertEqual(math.fmax(NINF, x), x) | |
| self.assertEqual(math.fmax(x, NINF), x) | |
| @requires_IEEE_754 | |
| def test_fmax_nans(self): | |
| # When exactly one operand is NaN, the other is returned. | |
| for x in [NINF, -1., -0., 0., 1., INF]: | |
| with self.subTest(x=x, is_negative=math.copysign(1, x) < 0): | |
| self.assertFalse(math.isnan(math.fmax(NAN, x))) | |
| self.assertFalse(math.isnan(math.fmax(x, NAN))) | |
| self.assertFalse(math.isnan(math.fmax(NNAN, x))) | |
| self.assertFalse(math.isnan(math.fmax(x, NNAN))) | |
| # When both operands are NaNs, fmax() returns NaN (see C11, F.10.9.2) | |
| # whose sign is implementation-defined (see C11, F.10.0.3). | |
| self.assertTrue(math.isnan(math.fmax(NAN, NAN))) | |
| self.assertTrue(math.isnan(math.fmax(NNAN, NNAN))) | |
| self.assertTrue(math.isnan(math.fmax(NAN, NNAN))) | |
| self.assertTrue(math.isnan(math.fmax(NNAN, NAN))) | |
| def test_fmin(self): | |
| self.assertRaises(TypeError, math.fmin) | |
| self.assertRaises(TypeError, math.fmin, 'x', 'y') | |
| self.assertEqual(math.fmin(0., 0.), 0.) | |
| self.assertEqual(math.fmin(1., 2.), 1.) | |
| self.assertEqual(math.fmin(2., 1.), 1.) | |
| self.assertEqual(math.fmin(+1., +0.), 0.) | |
| self.assertEqual(math.fmin(+0., +1.), 0.) | |
| self.assertEqual(math.fmin(+1., -0.), 0.) | |
| self.assertEqual(math.fmin(-0., +1.), 0.) | |
| self.assertEqual(math.fmin(-1., +0.), -1.) | |
| self.assertEqual(math.fmin(+0., -1.), -1.) | |
| self.assertEqual(math.fmin(-1., -0.), -1.) | |
| self.assertEqual(math.fmin(-0., -1.), -1.) | |
| for x in [NINF, -1., -0., 0., 1., INF]: | |
| self.assertFalse(math.isnan(x)) | |
| with self.subTest(x=x, is_negative=math.copysign(1, x) < 0): | |
| self.assertEqual(math.fmin(INF, x), x) | |
| self.assertEqual(math.fmin(x, INF), x) | |
| self.assertEqual(math.fmin(NINF, x), NINF) | |
| self.assertEqual(math.fmin(x, NINF), NINF) | |
| @requires_IEEE_754 | |
| def test_fmin_nans(self): | |
| # When exactly one operand is NaN, the other is returned. | |
| for x in [NINF, -1., -0., 0., 1., INF]: | |
| with self.subTest(x=x, is_negative=math.copysign(1, x) < 0): | |
| self.assertFalse(math.isnan(math.fmin(NAN, x))) | |
| self.assertFalse(math.isnan(math.fmin(x, NAN))) | |
| self.assertFalse(math.isnan(math.fmin(NNAN, x))) | |
| self.assertFalse(math.isnan(math.fmin(x, NNAN))) | |
| # When both operands are NaNs, fmin() returns NaN (see C11, F.10.9.3) | |
| # whose sign is implementation-defined (see C11, F.10.0.3). | |
| self.assertTrue(math.isnan(math.fmin(NAN, NAN))) | |
| self.assertTrue(math.isnan(math.fmin(NNAN, NNAN))) | |
| self.assertTrue(math.isnan(math.fmin(NAN, NNAN))) | |
| self.assertTrue(math.isnan(math.fmin(NNAN, NAN))) | |
| def testFrexp(self): | |
| self.assertRaises(TypeError, math.frexp) | |
| def testfrexp(name, result, expected): | |
| (mant, exp), (emant, eexp) = result, expected | |
| if abs(mant-emant) > eps or exp != eexp: | |
| self.fail('%s returned %r, expected %r'%\ | |
| (name, result, expected)) | |
| testfrexp('frexp(-1)', math.frexp(-1), (-0.5, 1)) | |
| testfrexp('frexp(0)', math.frexp(0), (0, 0)) | |
| testfrexp('frexp(1)', math.frexp(1), (0.5, 1)) | |
| testfrexp('frexp(2)', math.frexp(2), (0.5, 2)) | |
| self.assertEqual(math.frexp(INF)[0], INF) | |
| self.assertEqual(math.frexp(NINF)[0], NINF) | |
| self.assertTrue(math.isnan(math.frexp(NAN)[0])) | |
| @requires_IEEE_754 | |
| @skip_if_double_rounding | |
| def testFsum(self): | |
| # math.fsum relies on exact rounding for correct operation. | |
| # There's a known problem with IA32 floating-point that causes | |
| # inexact rounding in some situations, and will cause the | |
| # math.fsum tests below to fail; see issue #2937. On non IEEE | |
| # 754 platforms, and on IEEE 754 platforms that exhibit the | |
| # problem described in issue #2937, we simply skip the whole | |
| # test. | |
| # Python version of math.fsum, for comparison. Uses a | |
| # different algorithm based on frexp, ldexp and integer | |
| # arithmetic. | |
| from sys import float_info | |
| mant_dig = float_info.mant_dig | |
| etiny = float_info.min_exp - mant_dig | |
| def msum(iterable): | |
| """Full precision summation. Compute sum(iterable) without any | |
| intermediate accumulation of error. Based on the 'lsum' function | |
| at https://code.activestate.com/recipes/393090-binary-floating-point-summation-accurate-to-full-p/ | |
| """ | |
| tmant, texp = 0, 0 | |
| for x in iterable: | |
| mant, exp = math.frexp(x) | |
| mant, exp = int(math.ldexp(mant, mant_dig)), exp - mant_dig | |
| if texp > exp: | |
| tmant <<= texp-exp | |
| texp = exp | |
| else: | |
| mant <<= exp-texp | |
| tmant += mant | |
| # Round tmant * 2**texp to a float. The original recipe | |
| # used float(str(tmant)) * 2.0**texp for this, but that's | |
| # a little unsafe because str -> float conversion can't be | |
| # relied upon to do correct rounding on all platforms. | |
| tail = max(len(bin(abs(tmant)))-2 - mant_dig, etiny - texp) | |
| if tail > 0: | |
| h = 1 << (tail-1) | |
| tmant = tmant // (2*h) + bool(tmant & h and tmant & 3*h-1) | |
| texp += tail | |
| return math.ldexp(tmant, texp) | |
| test_values = [ | |
| ([], 0.0), | |
| ([0.0], 0.0), | |
| ([1e100, 1.0, -1e100, 1e-100, 1e50, -1.0, -1e50], 1e-100), | |
| ([1e100, 1.0, -1e100, 1e-100, 1e50, -1, -1e50], 1e-100), | |
| ([2.0**53, -0.5, -2.0**-54], 2.0**53-1.0), | |
| ([2.0**53, 1.0, 2.0**-100], 2.0**53+2.0), | |
| ([2.0**53+10.0, 1.0, 2.0**-100], 2.0**53+12.0), | |
| ([2.0**53-4.0, 0.5, 2.0**-54], 2.0**53-3.0), | |
| ([1./n for n in range(1, 1001)], | |
| float.fromhex('0x1.df11f45f4e61ap+2')), | |
| ([(-1.)**n/n for n in range(1, 1001)], | |
| float.fromhex('-0x1.62a2af1bd3624p-1')), | |
| ([1e16, 1., 1e-16], 10000000000000002.0), | |
| ([1e16-2., 1.-2.**-53, -(1e16-2.), -(1.-2.**-53)], 0.0), | |
| # exercise code for resizing partials array | |
| ([2.**n - 2.**(n+50) + 2.**(n+52) for n in range(-1074, 972, 2)] + | |
| [-2.**1022], | |
| float.fromhex('0x1.5555555555555p+970')), | |
| ] | |
| # Telescoping sum, with exact differences (due to Sterbenz) | |
| terms = [1.7**i for i in range(1001)] | |
| test_values.append(( | |
| [terms[i+1] - terms[i] for i in range(1000)] + [-terms[1000]], | |
| -terms[0] | |
| )) | |
| for i, (vals, expected) in enumerate(test_values): | |
| try: | |
| actual = math.fsum(vals) | |
| except OverflowError: | |
| self.fail("test %d failed: got OverflowError, expected %r " | |
| "for math.fsum(%.100r)" % (i, expected, vals)) | |
| except ValueError: | |
| self.fail("test %d failed: got ValueError, expected %r " | |
| "for math.fsum(%.100r)" % (i, expected, vals)) | |
| self.assertEqual(actual, expected) | |
| from random import random, gauss, shuffle | |
| for j in range(1000): | |
| vals = [7, 1e100, -7, -1e100, -9e-20, 8e-20] * 10 | |
| s = 0 | |
| for i in range(200): | |
| v = gauss(0, random()) ** 7 - s | |
| s += v | |
| vals.append(v) | |
| shuffle(vals) | |
| s = msum(vals) | |
| self.assertEqual(msum(vals), math.fsum(vals)) | |
| self.assertEqual(math.fsum([1.0, math.inf]), math.inf) | |
| self.assertTrue(math.isnan(math.fsum([math.nan, 1.0]))) | |
| self.assertEqual(math.fsum([1e100, FloatLike(1.0), -1e100, 1e-100, | |
| 1e50, FloatLike(-1.0), -1e50]), 1e-100) | |
| self.assertRaises(OverflowError, math.fsum, [1e+308, 1e+308]) | |
| self.assertRaises(ValueError, math.fsum, [math.inf, -math.inf]) | |
| self.assertRaises(TypeError, math.fsum, ['spam']) | |
| self.assertRaises(TypeError, math.fsum, 1) | |
| self.assertRaises(OverflowError, math.fsum, [10**1000]) | |
| def bad_iter(): | |
| yield 1.0 | |
| raise ZeroDivisionError | |
| self.assertRaises(ZeroDivisionError, math.fsum, bad_iter()) | |
| def testGcd(self): | |
| gcd = math.gcd | |
| self.assertEqual(gcd(0, 0), 0) | |
| self.assertEqual(gcd(1, 0), 1) | |
| self.assertEqual(gcd(-1, 0), 1) | |
| self.assertEqual(gcd(0, 1), 1) | |
| self.assertEqual(gcd(0, -1), 1) | |
| self.assertEqual(gcd(7, 1), 1) | |
| self.assertEqual(gcd(7, -1), 1) | |
| self.assertEqual(gcd(-23, 15), 1) | |
| self.assertEqual(gcd(120, 84), 12) | |
| self.assertEqual(gcd(84, -120), 12) | |
| self.assertEqual(gcd(1216342683557601535506311712, | |
| 436522681849110124616458784), 32) | |
| x = 434610456570399902378880679233098819019853229470286994367836600566 | |
| y = 1064502245825115327754847244914921553977 | |
| for c in (652560, | |
| 576559230871654959816130551884856912003141446781646602790216406874): | |
| a = x * c | |
| b = y * c | |
| self.assertEqual(gcd(a, b), c) | |
| self.assertEqual(gcd(b, a), c) | |
| self.assertEqual(gcd(-a, b), c) | |
| self.assertEqual(gcd(b, -a), c) | |
| self.assertEqual(gcd(a, -b), c) | |
| self.assertEqual(gcd(-b, a), c) | |
| self.assertEqual(gcd(-a, -b), c) | |
| self.assertEqual(gcd(-b, -a), c) | |
| self.assertEqual(gcd(), 0) | |
| self.assertEqual(gcd(120), 120) | |
| self.assertEqual(gcd(-120), 120) | |
| self.assertEqual(gcd(120, 84, 102), 6) | |
| self.assertEqual(gcd(120, 1, 84), 1) | |
| self.assertRaises(TypeError, gcd, 120.0) | |
| self.assertRaises(TypeError, gcd, 120.0, 84) | |
| self.assertRaises(TypeError, gcd, 120, 84.0) | |
| self.assertRaises(TypeError, gcd, 120, 1, 84.0) | |
| self.assertEqual(gcd(MyIndexable(120), MyIndexable(84)), 12) | |
| def testHypot(self): | |
| from decimal import Decimal | |
| from fractions import Fraction | |
| hypot = math.hypot | |
| # Test different numbers of arguments (from zero to five) | |
| # against a straightforward pure python implementation | |
| args = math.e, math.pi, math.sqrt(2.0), math.gamma(3.5), math.sin(2.1) | |
| for i in range(len(args)+1): | |
| self.assertAlmostEqual( | |
| hypot(*args[:i]), | |
| math.sqrt(sum(s**2 for s in args[:i])) | |
| ) | |
| # Test allowable types (those with __float__) | |
| self.assertEqual(hypot(12.0, 5.0), 13.0) | |
| self.assertEqual(hypot(12, 5), 13) | |
| self.assertEqual(hypot(0.75, -1), 1.25) | |
| self.assertEqual(hypot(-1, 0.75), 1.25) | |
| self.assertEqual(hypot(0.75, FloatLike(-1.)), 1.25) | |
| self.assertEqual(hypot(FloatLike(-1.), 0.75), 1.25) | |
| self.assertEqual(hypot(Decimal(12), Decimal(5)), 13) | |
| self.assertEqual(hypot(Fraction(12, 32), Fraction(5, 32)), Fraction(13, 32)) | |
| self.assertEqual(hypot(True, False, True, True, True), 2.0) | |
| # Test corner cases | |
| self.assertEqual(hypot(0.0, 0.0), 0.0) # Max input is zero | |
| self.assertEqual(hypot(-10.5), 10.5) # Negative input | |
| self.assertEqual(hypot(), 0.0) # Negative input | |
| self.assertEqual(1.0, | |
| math.copysign(1.0, hypot(-0.0)) # Convert negative zero to positive zero | |
| ) | |
| self.assertEqual( # Handling of moving max to the end | |
| hypot(1.5, 1.5, 0.5), | |
| hypot(1.5, 0.5, 1.5), | |
| ) | |
| # Test handling of bad arguments | |
| with self.assertRaises(TypeError): # Reject keyword args | |
| hypot(x=1) | |
| with self.assertRaises(TypeError): # Reject values without __float__ | |
| hypot(1.1, 'string', 2.2) | |
| int_too_big_for_float = 10 ** (sys.float_info.max_10_exp + 5) | |
| with self.assertRaises((ValueError, OverflowError)): | |
| hypot(1, int_too_big_for_float) | |
| # Any infinity gives positive infinity. | |
| self.assertEqual(hypot(INF), INF) | |
| self.assertEqual(hypot(0, INF), INF) | |
| self.assertEqual(hypot(10, INF), INF) | |
| self.assertEqual(hypot(-10, INF), INF) | |
| self.assertEqual(hypot(NAN, INF), INF) | |
| self.assertEqual(hypot(INF, NAN), INF) | |
| self.assertEqual(hypot(NINF, NAN), INF) | |
| self.assertEqual(hypot(NAN, NINF), INF) | |
| self.assertEqual(hypot(-INF, INF), INF) | |
| self.assertEqual(hypot(-INF, -INF), INF) | |
| self.assertEqual(hypot(10, -INF), INF) | |
| # If no infinity, any NaN gives a NaN. | |
| self.assertTrue(math.isnan(hypot(NAN))) | |
| self.assertTrue(math.isnan(hypot(0, NAN))) | |
| self.assertTrue(math.isnan(hypot(NAN, 10))) | |
| self.assertTrue(math.isnan(hypot(10, NAN))) | |
| self.assertTrue(math.isnan(hypot(NAN, NAN))) | |
| self.assertTrue(math.isnan(hypot(NAN))) | |
| # Verify scaling for extremely large values | |
| fourthmax = FLOAT_MAX / 4.0 | |
| for n in range(32): | |
| self.assertTrue(math.isclose(hypot(*([fourthmax]*n)), | |
| fourthmax * math.sqrt(n))) | |
| # Verify scaling for extremely small values | |
| for exp in range(32): | |
| scale = FLOAT_MIN / 2.0 ** exp | |
| self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) | |
| self.assertRaises(TypeError, math.hypot, *([1.0]*18), 'spam') | |
| @requires_IEEE_754 | |
| @skip_if_double_rounding | |
| @support.skip_on_newlib | |
| def testHypotAccuracy(self): | |
| # Verify improved accuracy in cases that were known to be inaccurate. | |
| # | |
| # The new algorithm's accuracy depends on IEEE 754 arithmetic | |
| # guarantees, on having the usual ROUND HALF EVEN rounding mode, on | |
| # the system not having double rounding due to extended precision, | |
| # and on the compiler maintaining the specified order of operations. | |
| # | |
| # This test is known to succeed on most of our builds. If it fails | |
| # some build, we either need to add another skipIf if the cause is | |
| # identifiable; otherwise, we can remove this test entirely. | |
| hypot = math.hypot | |
| Decimal = decimal.Decimal | |
| high_precision = decimal.Context(prec=500) | |
| for hx, hy in [ | |
| # Cases with a 1 ulp error in Python 3.7 compiled with Clang | |
| ('0x1.10e89518dca48p+29', '0x1.1970f7565b7efp+30'), | |
| ('0x1.10106eb4b44a2p+29', '0x1.ef0596cdc97f8p+29'), | |
| ('0x1.459c058e20bb7p+30', '0x1.993ca009b9178p+29'), | |
| ('0x1.378371ae67c0cp+30', '0x1.fbe6619854b4cp+29'), | |
| ('0x1.f4cd0574fb97ap+29', '0x1.50fe31669340ep+30'), | |
| ('0x1.494b2cdd3d446p+29', '0x1.212a5367b4c7cp+29'), | |
| ('0x1.f84e649f1e46dp+29', '0x1.1fa56bef8eec4p+30'), | |
| ('0x1.2e817edd3d6fap+30', '0x1.eb0814f1e9602p+29'), | |
| ('0x1.0d3a6e3d04245p+29', '0x1.32a62fea52352p+30'), | |
| ('0x1.888e19611bfc5p+29', '0x1.52b8e70b24353p+29'), | |
| # Cases with 2 ulp error in Python 3.8 | |
| ('0x1.538816d48a13fp+29', '0x1.7967c5ca43e16p+29'), | |
| ('0x1.57b47b7234530p+29', '0x1.74e2c7040e772p+29'), | |
| ('0x1.821b685e9b168p+30', '0x1.677dc1c1e3dc6p+29'), | |
| ('0x1.9e8247f67097bp+29', '0x1.24bd2dc4f4baep+29'), | |
| ('0x1.b73b59e0cb5f9p+29', '0x1.da899ab784a97p+28'), | |
| ('0x1.94a8d2842a7cfp+30', '0x1.326a51d4d8d8ap+30'), | |
| ('0x1.e930b9cd99035p+29', '0x1.5a1030e18dff9p+30'), | |
| ('0x1.1592bbb0e4690p+29', '0x1.a9c337b33fb9ap+29'), | |
| ('0x1.1243a50751fd4p+29', '0x1.a5a10175622d9p+29'), | |
| ('0x1.57a8596e74722p+30', '0x1.42d1af9d04da9p+30'), | |
| # Cases with 1 ulp error in version fff3c28052e6b0 | |
| ('0x1.ee7dbd9565899p+29', '0x1.7ab4d6fc6e4b4p+29'), | |
| ('0x1.5c6bfbec5c4dcp+30', '0x1.02511184b4970p+30'), | |
| ('0x1.59dcebba995cap+30', '0x1.50ca7e7c38854p+29'), | |
| ('0x1.768cdd94cf5aap+29', '0x1.9cfdc5571d38ep+29'), | |
| ('0x1.dcf137d60262ep+29', '0x1.1101621990b3ep+30'), | |
| ('0x1.3a2d006e288b0p+30', '0x1.e9a240914326cp+29'), | |
| ('0x1.62a32f7f53c61p+29', '0x1.47eb6cd72684fp+29'), | |
| ('0x1.d3bcb60748ef2p+29', '0x1.3f13c4056312cp+30'), | |
| ('0x1.282bdb82f17f3p+30', '0x1.640ba4c4eed3ap+30'), | |
| ('0x1.89d8c423ea0c6p+29', '0x1.d35dcfe902bc3p+29'), | |
| ]: | |
| x = float.fromhex(hx) | |
| y = float.fromhex(hy) | |
| with self.subTest(hx=hx, hy=hy, x=x, y=y): | |
| with decimal.localcontext(high_precision): | |
| z = float((Decimal(x)**2 + Decimal(y)**2).sqrt()) | |
| self.assertEqual(hypot(x, y), z) | |
| def testDist(self): | |
| from decimal import Decimal as D | |
| from fractions import Fraction as F | |
| dist = math.dist | |
| sqrt = math.sqrt | |
| # Simple exact cases | |
| self.assertEqual(dist((1.0, 2.0, 3.0), (4.0, 2.0, -1.0)), 5.0) | |
| self.assertEqual(dist((1, 2, 3), (4, 2, -1)), 5.0) | |
| # Test different numbers of arguments (from zero to nine) | |
| # against a straightforward pure python implementation | |
| for i in range(9): | |
| for j in range(5): | |
| p = tuple(random.uniform(-5, 5) for k in range(i)) | |
| q = tuple(random.uniform(-5, 5) for k in range(i)) | |
| self.assertAlmostEqual( | |
| dist(p, q), | |
| sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) | |
| ) | |
| # Test non-tuple inputs | |
| self.assertEqual(dist([1.0, 2.0, 3.0], [4.0, 2.0, -1.0]), 5.0) | |
| self.assertEqual(dist(iter([1.0, 2.0, 3.0]), iter([4.0, 2.0, -1.0])), 5.0) | |
| # Test allowable types (those with __float__) | |
| self.assertEqual(dist((14.0, 1.0), (2.0, -4.0)), 13.0) | |
| self.assertEqual(dist((14, 1), (2, -4)), 13) | |
| self.assertEqual(dist((FloatLike(14.), 1), (2, -4)), 13) | |
| self.assertEqual(dist((11, 1), (FloatLike(-1.), -4)), 13) | |
| self.assertEqual(dist((14, FloatLike(-1.)), (2, -6)), 13) | |
| self.assertEqual(dist((14, -1), (2, -6)), 13) | |
| self.assertEqual(dist((D(14), D(1)), (D(2), D(-4))), D(13)) | |
| self.assertEqual(dist((F(14, 32), F(1, 32)), (F(2, 32), F(-4, 32))), | |
| F(13, 32)) | |
| self.assertEqual(dist((True, True, False, False, True, True), | |
| (True, False, True, False, False, False)), | |
| 2.0) | |
| # Test corner cases | |
| self.assertEqual(dist((13.25, 12.5, -3.25), | |
| (13.25, 12.5, -3.25)), | |
| 0.0) # Distance with self is zero | |
| self.assertEqual(dist((), ()), 0.0) # Zero-dimensional case | |
| self.assertEqual(1.0, # Convert negative zero to positive zero | |
| math.copysign(1.0, dist((-0.0,), (0.0,))) | |
| ) | |
| self.assertEqual(1.0, # Convert negative zero to positive zero | |
| math.copysign(1.0, dist((0.0,), (-0.0,))) | |
| ) | |
| self.assertEqual( # Handling of moving max to the end | |
| dist((1.5, 1.5, 0.5), (0, 0, 0)), | |
| dist((1.5, 0.5, 1.5), (0, 0, 0)) | |
| ) | |
| # Verify tuple subclasses are allowed | |
| class T(tuple): | |
| pass | |
| self.assertEqual(dist(T((1, 2, 3)), ((4, 2, -1))), 5.0) | |
| # Test handling of bad arguments | |
| with self.assertRaises(TypeError): # Reject keyword args | |
| dist(p=(1, 2, 3), q=(4, 5, 6)) | |
| with self.assertRaises(TypeError): # Too few args | |
| dist((1, 2, 3)) | |
| with self.assertRaises(TypeError): # Too many args | |
| dist((1, 2, 3), (4, 5, 6), (7, 8, 9)) | |
| with self.assertRaises(TypeError): # Scalars not allowed | |
| dist(1, 2) | |
| with self.assertRaises(TypeError): # Reject values without __float__ | |
| dist((1.1, 'string', 2.2), (1, 2, 3)) | |
| with self.assertRaises(ValueError): # Check dimension agree | |
| dist((1, 2, 3, 4), (5, 6, 7)) | |
| with self.assertRaises(ValueError): # Check dimension agree | |
| dist((1, 2, 3), (4, 5, 6, 7)) | |
| with self.assertRaises(TypeError): | |
| dist((1,)*17 + ("spam",), (1,)*18) | |
| with self.assertRaises(TypeError): # Rejects invalid types | |
| dist("abc", "xyz") | |
| int_too_big_for_float = 10 ** (sys.float_info.max_10_exp + 5) | |
| with self.assertRaises((ValueError, OverflowError)): | |
| dist((1, int_too_big_for_float), (2, 3)) | |
| with self.assertRaises((ValueError, OverflowError)): | |
| dist((2, 3), (1, int_too_big_for_float)) | |
| with self.assertRaises(TypeError): | |
| dist((1,), 2) | |
| with self.assertRaises(TypeError): | |
| dist([1], 2) | |
| class BadFloat: | |
| __float__ = BadDescr() | |
| with self.assertRaises(ValueError): | |
| dist([1], [BadFloat()]) | |
| # Verify that the one dimensional case is equivalent to abs() | |
| for i in range(20): | |
| p, q = random.random(), random.random() | |
| self.assertEqual(dist((p,), (q,)), abs(p - q)) | |
| # Test special values | |
| values = [NINF, -10.5, -0.0, 0.0, 10.5, INF, NAN] | |
| for p in itertools.product(values, repeat=3): | |
| for q in itertools.product(values, repeat=3): | |
| diffs = [px - qx for px, qx in zip(p, q)] | |
| if any(map(math.isinf, diffs)): | |
| # Any infinite difference gives positive infinity. | |
| self.assertEqual(dist(p, q), INF) | |
| elif any(map(math.isnan, diffs)): | |
| # If no infinity, any NaN gives a NaN. | |
| self.assertTrue(math.isnan(dist(p, q))) | |
| # Verify scaling for extremely large values | |
| fourthmax = FLOAT_MAX / 4.0 | |
| for n in range(32): | |
| p = (fourthmax,) * n | |
| q = (0.0,) * n | |
| self.assertTrue(math.isclose(dist(p, q), fourthmax * math.sqrt(n))) | |
| self.assertTrue(math.isclose(dist(q, p), fourthmax * math.sqrt(n))) | |
| # Verify scaling for extremely small values | |
| for exp in range(32): | |
| scale = FLOAT_MIN / 2.0 ** exp | |
| p = (4*scale, 3*scale) | |
| q = (0.0, 0.0) | |
| self.assertEqual(math.dist(p, q), 5*scale) | |
| self.assertEqual(math.dist(q, p), 5*scale) | |
| def test_math_dist_leak(self): | |
| # gh-98897: Check for error handling does not leak memory | |
| with self.assertRaises(ValueError): | |
| math.dist([1, 2], [3, 4, 5]) | |
| def test_lcm(self): | |
| lcm = math.lcm | |
| self.assertEqual(lcm(0, 0), 0) | |
| self.assertEqual(lcm(1, 0), 0) | |
| self.assertEqual(lcm(-1, 0), 0) | |
| self.assertEqual(lcm(0, 1), 0) | |
| self.assertEqual(lcm(0, -1), 0) | |
| self.assertEqual(lcm(7, 1), 7) | |
| self.assertEqual(lcm(7, -1), 7) | |
| self.assertEqual(lcm(-23, 15), 345) | |
| self.assertEqual(lcm(120, 84), 840) | |
| self.assertEqual(lcm(84, -120), 840) | |
| self.assertEqual(lcm(1216342683557601535506311712, | |
| 436522681849110124616458784), | |
| 16592536571065866494401400422922201534178938447014944) | |
| x = 43461045657039990237 | |
| y = 10645022458251153277 | |
| for c in (652560, | |
| 57655923087165495981): | |
| a = x * c | |
| b = y * c | |
| d = x * y * c | |
| self.assertEqual(lcm(a, b), d) | |
| self.assertEqual(lcm(b, a), d) | |
| self.assertEqual(lcm(-a, b), d) | |
| self.assertEqual(lcm(b, -a), d) | |
| self.assertEqual(lcm(a, -b), d) | |
| self.assertEqual(lcm(-b, a), d) | |
| self.assertEqual(lcm(-a, -b), d) | |
| self.assertEqual(lcm(-b, -a), d) | |
| self.assertEqual(lcm(), 1) | |
| self.assertEqual(lcm(120), 120) | |
| self.assertEqual(lcm(-120), 120) | |
| self.assertEqual(lcm(120, 84, 102), 14280) | |
| self.assertEqual(lcm(120, 0, 84), 0) | |
| self.assertRaises(TypeError, lcm, 120.0) | |
| self.assertRaises(TypeError, lcm, 120.0, 84) | |
| self.assertRaises(TypeError, lcm, 120, 84.0) | |
| self.assertRaises(TypeError, lcm, 120, 0, 84.0) | |
| self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840) | |
| def testLdexp(self): | |
| self.assertRaises(TypeError, math.ldexp) | |
| self.assertRaises(TypeError, math.ldexp, 2.0, 1.1) | |
| self.ftest('ldexp(0,1)', math.ldexp(0,1), 0) | |
| self.ftest('ldexp(1,1)', math.ldexp(1,1), 2) | |
| self.ftest('ldexp(1,-1)', math.ldexp(1,-1), 0.5) | |
| self.ftest('ldexp(-1,1)', math.ldexp(-1,1), -2) | |
| self.assertRaises(OverflowError, math.ldexp, 1., 1000000) | |
| self.assertRaises(OverflowError, math.ldexp, -1., 1000000) | |
| self.assertEqual(math.ldexp(1., -1000000), 0.) | |
| self.assertEqual(math.ldexp(-1., -1000000), -0.) | |
| self.assertEqual(math.ldexp(INF, 30), INF) | |
| self.assertEqual(math.ldexp(NINF, -213), NINF) | |
| self.assertTrue(math.isnan(math.ldexp(NAN, 0))) | |
| # large second argument | |
| for n in [10**5, 10**10, 10**20, 10**40]: | |
| self.assertEqual(math.ldexp(INF, -n), INF) | |
| self.assertEqual(math.ldexp(NINF, -n), NINF) | |
| self.assertEqual(math.ldexp(1., -n), 0.) | |
| self.assertEqual(math.ldexp(-1., -n), -0.) | |
| self.assertEqual(math.ldexp(0., -n), 0.) | |
| self.assertEqual(math.ldexp(-0., -n), -0.) | |
| self.assertTrue(math.isnan(math.ldexp(NAN, -n))) | |
| self.assertRaises(OverflowError, math.ldexp, 1., n) | |
| self.assertRaises(OverflowError, math.ldexp, -1., n) | |
| self.assertEqual(math.ldexp(0., n), 0.) | |
| self.assertEqual(math.ldexp(-0., n), -0.) | |
| self.assertEqual(math.ldexp(INF, n), INF) | |
| self.assertEqual(math.ldexp(NINF, n), NINF) | |
| self.assertTrue(math.isnan(math.ldexp(NAN, n))) | |
| @requires_IEEE_754 | |
| def testLdexp_denormal(self): | |
| # Denormal output incorrectly rounded (truncated) | |
| # on some Windows. | |
| self.assertEqual(math.ldexp(6993274598585239, -1126), 1e-323) | |
| def testLog(self): | |
| self.assertRaises(TypeError, math.log) | |
| self.assertRaises(TypeError, math.log, 1, 2, 3) | |
| self.ftest('log(1/e)', math.log(1/math.e), -1) | |
| self.ftest('log(1)', math.log(1), 0) | |
| self.ftest('log(e)', math.log(math.e), 1) | |
| self.ftest('log(32,2)', math.log(32,2), 5) | |
| self.ftest('log(10**40, 10)', math.log(10**40, 10), 40) | |
| self.ftest('log(10**40, 10**20)', math.log(10**40, 10**20), 2) | |
| self.ftest('log(10**1000)', math.log(10**1000), | |
| 2302.5850929940457) | |
| self.ftest('log(10**2000, 10**1000)', math.log(10**2000, 10**1000), 2) | |
| self.ftest('log(MyIndexable(32), MyIndexable(2))', | |
| math.log(MyIndexable(32), MyIndexable(2)), 5) | |
| self.ftest('log(MyIndexable(10**1000))', | |
| math.log(MyIndexable(10**1000)), | |
| 2302.5850929940457) | |
| self.ftest('log(MyIndexable(10**2000), MyIndexable(10**1000))', | |
| math.log(MyIndexable(10**2000), MyIndexable(10**1000)), | |
| 2) | |
| self.assertRaises(ValueError, math.log, 0.0) | |
| self.assertRaises(ValueError, math.log, 0) | |
| self.assertRaises(ValueError, math.log, MyIndexable(0)) | |
| self.assertRaises(ValueError, math.log, -1.5) | |
| self.assertRaises(ValueError, math.log, -1) | |
| self.assertRaises(ValueError, math.log, MyIndexable(-1)) | |
| self.assertRaises(ValueError, math.log, -10**1000) | |
| self.assertRaises(ValueError, math.log, MyIndexable(-10**1000)) | |
| self.assertRaises(ValueError, math.log, 10, -10) | |
| self.assertRaises(ValueError, math.log, NINF) | |
| self.assertEqual(math.log(INF), INF) | |
| self.assertTrue(math.isnan(math.log(NAN))) | |
| self.assertEqual(math.log(IndexableFloatLike(math.e, 10**1000)), 1.0) | |
| self.assertAlmostEqual(math.log(IndexableFloatLike(OverflowError(), 10**1000)), | |
| 2302.5850929940457) | |
| def testLog1p(self): | |
| self.assertRaises(TypeError, math.log1p) | |
| for n in [2, 2**90, 2**300]: | |
| self.assertAlmostEqual(math.log1p(n), math.log1p(float(n))) | |
| self.assertRaises(ValueError, math.log1p, -1) | |
| self.assertEqual(math.log1p(INF), INF) | |
| @requires_IEEE_754 | |
| def testLog2(self): | |
| self.assertRaises(TypeError, math.log2) | |
| # Check some integer values | |
| self.assertEqual(math.log2(1), 0.0) | |
| self.assertEqual(math.log2(2), 1.0) | |
| self.assertEqual(math.log2(4), 2.0) | |
| self.assertEqual(math.log2(MyIndexable(4)), 2.0) | |
| self.assertRaises(ValueError, math.log2, 0.0) | |
| self.assertRaises(ValueError, math.log2, 0) | |
| self.assertRaises(ValueError, math.log2, MyIndexable(0)) | |
| self.assertRaises(ValueError, math.log2, -1.5) | |
| self.assertRaises(ValueError, math.log2, -1) | |
| self.assertRaises(ValueError, math.log2, MyIndexable(-1)) | |
| self.assertRaises(ValueError, math.log2, -2**2000) | |
| self.assertRaises(ValueError, math.log2, MyIndexable(-2**2000)) | |
| self.assertRaises(ValueError, math.log2, NINF) | |
| self.assertTrue(math.isnan(math.log2(NAN))) | |
| self.assertEqual(math.log2(IndexableFloatLike(8.0, 2**2000)), 3.0) | |
| self.assertEqual(math.log2(IndexableFloatLike(OverflowError(), 2**2000)), 2000.0) | |
| @requires_IEEE_754 | |
| # log2() is not accurate enough on Mac OS X Tiger (10.4) | |
| @support.requires_mac_ver(10, 5) | |
| @support.skip_on_newlib | |
| def testLog2Exact(self): | |
| # Check that we get exact equality for log2 of powers of 2. | |
| actual = [math.log2(math.ldexp(1.0, n)) for n in range(-1074, 1024)] | |
| expected = [float(n) for n in range(-1074, 1024)] | |
| self.assertEqual(actual, expected) | |
| # Large integer values | |
| self.assertEqual(math.log2(2**1023), 1023.0) | |
| self.assertEqual(math.log2(2**1024), 1024.0) | |
| self.assertEqual(math.log2(2**2000), 2000.0) | |
| self.assertEqual(math.log2(MyIndexable(2**2000)), 2000.0) | |
| def testLog10(self): | |
| self.assertRaises(TypeError, math.log10) | |
| self.ftest('log10(0.1)', math.log10(0.1), -1) | |
| self.ftest('log10(1)', math.log10(1), 0) | |
| self.ftest('log10(10)', math.log10(10), 1) | |
| self.ftest('log10(10**1000)', math.log10(10**1000), 1000.0) | |
| self.ftest('log10(MyIndexable(10))', math.log10(MyIndexable(10)), 1) | |
| self.ftest('log10(MyIndexable(10**1000))', | |
| math.log10(MyIndexable(10**1000)), 1000.0) | |
| self.assertRaises(ValueError, math.log10, 0.0) | |
| self.assertRaises(ValueError, math.log10, 0) | |
| self.assertRaises(ValueError, math.log10, MyIndexable(0)) | |
| self.assertRaises(ValueError, math.log10, -1.5) | |
| self.assertRaises(ValueError, math.log10, -1) | |
| self.assertRaises(ValueError, math.log10, MyIndexable(-1)) | |
| self.assertRaises(ValueError, math.log10, -10**1000) | |
| self.assertRaises(ValueError, math.log10, MyIndexable(-10**1000)) | |
| self.assertRaises(ValueError, math.log10, NINF) | |
| self.assertEqual(math.log(INF), INF) | |
| self.assertTrue(math.isnan(math.log10(NAN))) | |
| self.assertEqual(math.log10(IndexableFloatLike(100.0, 10**1000)), 2.0) | |
| self.assertEqual(math.log10(IndexableFloatLike(OverflowError(), 10**1000)), 1000.0) | |
| @support.bigmemtest(2**32, memuse=0.2) | |
| def test_log_huge_integer(self, size): | |
| v = 1 << size | |
| self.assertAlmostEqual(math.log2(v), size) | |
| self.assertAlmostEqual(math.log(v), size * 0.6931471805599453) | |
| self.assertAlmostEqual(math.log10(v), size * 0.3010299956639812) | |
| def testSumProd(self): | |
| sumprod = math.sumprod | |
| Decimal = decimal.Decimal | |
| Fraction = fractions.Fraction | |
| # Core functionality | |
| self.assertEqual(sumprod(iter([10, 20, 30]), (1, 2, 3)), 140) | |
| self.assertEqual(sumprod([1.5, 2.5], [3.5, 4.5]), 16.5) | |
| self.assertEqual(sumprod([], []), 0) | |
| self.assertEqual(sumprod([-1], [1.]), -1) | |
| self.assertEqual(sumprod([1.], [-1]), -1) | |
| # Type preservation and coercion | |
| for v in [ | |
| (10, 20, 30), | |
| (1.5, -2.5), | |
| (Fraction(3, 5), Fraction(4, 5)), | |
| (Decimal(3.5), Decimal(4.5)), | |
| (2.5, 10), # float/int | |
| (2.5, Fraction(3, 5)), # float/fraction | |
| (25, Fraction(3, 5)), # int/fraction | |
| (25, Decimal(4.5)), # int/decimal | |
| ]: | |
| for p, q in [(v, v), (v, v[::-1])]: | |
| with self.subTest(p=p, q=q): | |
| expected = sum(p_i * q_i for p_i, q_i in zip(p, q, strict=True)) | |
| actual = sumprod(p, q) | |
| self.assertEqual(expected, actual) | |
| self.assertEqual(type(expected), type(actual)) | |
| # Bad arguments | |
| self.assertRaises(TypeError, sumprod) # No args | |
| self.assertRaises(TypeError, sumprod, []) # One arg | |
| self.assertRaises(TypeError, sumprod, [], [], []) # Three args | |
| self.assertRaises(TypeError, sumprod, None, [10]) # Non-iterable | |
| self.assertRaises(TypeError, sumprod, [10], None) # Non-iterable | |
| self.assertRaises(TypeError, sumprod, ['x'], [1.0]) | |
| # Uneven lengths | |
| self.assertRaises(ValueError, sumprod, [10, 20], [30]) | |
| self.assertRaises(ValueError, sumprod, [10], [20, 30]) | |
| # Overflows | |
| self.assertEqual(sumprod([10**20], [1]), 10**20) | |
| self.assertEqual(sumprod([1], [10**20]), 10**20) | |
| self.assertEqual(sumprod([10**10], [10**10]), 10**20) | |
| self.assertEqual(sumprod([10**7]*10**5, [10**7]*10**5), 10**19) | |
| self.assertRaises(OverflowError, sumprod, [10**1000], [1.0]) | |
| self.assertRaises(OverflowError, sumprod, [1.0], [10**1000]) | |
| # Error in iterator | |
| def raise_after(n): | |
| for i in range(n): | |
| yield i | |
| raise RuntimeError | |
| with self.assertRaises(RuntimeError): | |
| sumprod(range(10), raise_after(5)) | |
| with self.assertRaises(RuntimeError): | |
| sumprod(raise_after(5), range(10)) | |
| from test.test_iter import BasicIterClass | |
| self.assertEqual(sumprod(BasicIterClass(1), [1]), 0) | |
| self.assertEqual(sumprod([1], BasicIterClass(1)), 0) | |
| # Error in multiplication | |
| class BadMultiply: | |
| def __mul__(self, other): | |
| raise RuntimeError | |
| def __rmul__(self, other): | |
| raise RuntimeError | |
| with self.assertRaises(RuntimeError): | |
| sumprod([10, BadMultiply(), 30], [1, 2, 3]) | |
| with self.assertRaises(RuntimeError): | |
| sumprod([1, 2, 3], [10, BadMultiply(), 30]) | |
| # Error in addition | |
| with self.assertRaises(TypeError): | |
| sumprod(['abc', 3], [5, 10]) | |
| with self.assertRaises(TypeError): | |
| sumprod([5, 10], ['abc', 3]) | |
| # Special values should give the same as the pure python recipe | |
| self.assertEqual(sumprod([10.1, math.inf], [20.2, 30.3]), math.inf) | |
| self.assertEqual(sumprod([10.1, math.inf], [math.inf, 30.3]), math.inf) | |
| self.assertEqual(sumprod([10.1, math.inf], [math.inf, math.inf]), math.inf) | |
| self.assertEqual(sumprod([10.1, -math.inf], [20.2, 30.3]), -math.inf) | |
| self.assertTrue(math.isnan(sumprod([10.1, math.inf], [-math.inf, math.inf]))) | |
| self.assertTrue(math.isnan(sumprod([10.1, math.nan], [20.2, 30.3]))) | |
| self.assertTrue(math.isnan(sumprod([10.1, math.inf], [math.nan, 30.3]))) | |
| self.assertTrue(math.isnan(sumprod([10.1, math.inf], [20.3, math.nan]))) | |
| # Error cases that arose during development | |
| args = ((-5, -5, 10), (1.5, 4611686018427387904, 2305843009213693952)) | |
| self.assertEqual(sumprod(*args), 0.0) | |
| @requires_IEEE_754 | |
| @skip_if_double_rounding | |
| @support.cpython_only # Other implementations may choose a different algorithm | |
| def test_sumprod_accuracy(self): | |
| sumprod = math.sumprod | |
| self.assertEqual(sumprod([0.1] * 10, [1]*10), 1.0) | |
| self.assertEqual(sumprod([0.1] * 20, [True, False] * 10), 1.0) | |
| self.assertEqual(sumprod([True, False] * 10, [0.1] * 20), 1.0) | |
| self.assertEqual(sumprod([1.0, 10E100, 1.0, -10E100], [1.0]*4), 2.0) | |
| @support.requires_resource('cpu') | |
| def test_sumprod_stress(self): | |
| sumprod = math.sumprod | |
| product = itertools.product | |
| Decimal = decimal.Decimal | |
| Fraction = fractions.Fraction | |
| class Int(int): | |
| def __add__(self, other): | |
| return Int(int(self) + int(other)) | |
| def __mul__(self, other): | |
| return Int(int(self) * int(other)) | |
| __radd__ = __add__ | |
| __rmul__ = __mul__ | |
| def __repr__(self): | |
| return f'Int({int(self)})' | |
| class Flt(float): | |
| def __add__(self, other): | |
| return Int(int(self) + int(other)) | |
| def __mul__(self, other): | |
| return Int(int(self) * int(other)) | |
| __radd__ = __add__ | |
| __rmul__ = __mul__ | |
| def __repr__(self): | |
| return f'Flt({int(self)})' | |
| def baseline_sumprod(p, q): | |
| """This defines the target behavior including exceptions and special values. | |
| However, it is subject to rounding errors, so float inputs should be exactly | |
| representable with only a few bits. | |
| """ | |
| total = 0 | |
| for p_i, q_i in zip(p, q, strict=True): | |
| total += p_i * q_i | |
| return total | |
| def run(func, *args): | |
| "Make comparing functions easier. Returns error status, type, and result." | |
| try: | |
| result = func(*args) | |
| except (AssertionError, NameError): | |
| raise | |
| except Exception as e: | |
| return type(e), None, 'None' | |
| return None, type(result), repr(result) | |
| pools = [ | |
| (-5, 10, -2**20, 2**31, 2**40, 2**61, 2**62, 2**80, 1.5, Int(7)), | |
| (5.25, -3.5, 4.75, 11.25, 400.5, 0.046875, 0.25, -1.0, -0.078125), | |
| (-19.0*2**500, 11*2**1000, -3*2**1500, 17*2*333, | |
| 5.25, -3.25, -3.0*2**(-333), 3, 2**513), | |
| (3.75, 2.5, -1.5, float('inf'), -float('inf'), float('NaN'), 14, | |
| 9, 3+4j, Flt(13), 0.0), | |
| (13.25, -4.25, Decimal('10.5'), Decimal('-2.25'), Fraction(13, 8), | |
| Fraction(-11, 16), 4.75 + 0.125j, 97, -41, Int(3)), | |
| (Decimal('6.125'), Decimal('12.375'), Decimal('-2.75'), Decimal(0), | |
| Decimal('Inf'), -Decimal('Inf'), Decimal('NaN'), 12, 13.5), | |
| (-2.0 ** -1000, 11*2**1000, 3, 7, -37*2**32, -2*2**-537, -2*2**-538, | |
| 2*2**-513), | |
| (-7 * 2.0 ** -510, 5 * 2.0 ** -520, 17, -19.0, -6.25), | |
| (11.25, -3.75, -0.625, 23.375, True, False, 7, Int(5)), | |
| ] | |
| for pool in pools: | |
| for size in range(4): | |
| for args1 in product(pool, repeat=size): | |
| for args2 in product(pool, repeat=size): | |
| args = (args1, args2) | |
| self.assertEqual( | |
| run(baseline_sumprod, *args), | |
| run(sumprod, *args), | |
| args, | |
| ) | |
| @requires_IEEE_754 | |
| @skip_if_double_rounding | |
| @support.cpython_only # Other implementations may choose a different algorithm | |
| @support.requires_resource('cpu') | |
| def test_sumprod_extended_precision_accuracy(self): | |
| import operator | |
| from fractions import Fraction | |
| from itertools import starmap | |
| from collections import namedtuple | |
| from math import log2, exp2, fabs | |
| from random import choices, uniform, shuffle | |
| from statistics import median | |
| DotExample = namedtuple('DotExample', ('x', 'y', 'target_sumprod', 'condition')) | |
| def DotExact(x, y): | |
| vec1 = map(Fraction, x) | |
| vec2 = map(Fraction, y) | |
| return sum(starmap(operator.mul, zip(vec1, vec2, strict=True))) | |
| def Condition(x, y): | |
| return 2.0 * DotExact(map(abs, x), map(abs, y)) / abs(DotExact(x, y)) | |
| def linspace(lo, hi, n): | |
| width = (hi - lo) / (n - 1) | |
| return [lo + width * i for i in range(n)] | |
| def GenDot(n, c): | |
| """ Algorithm 6.1 (GenDot) works as follows. The condition number (5.7) of | |
| the dot product xT y is proportional to the degree of cancellation. In | |
| order to achieve a prescribed cancellation, we generate the first half of | |
| the vectors x and y randomly within a large exponent range. This range is | |
| chosen according to the anticipated condition number. The second half of x | |
| and y is then constructed choosing xi randomly with decreasing exponent, | |
| and calculating yi such that some cancellation occurs. Finally, we permute | |
| the vectors x, y randomly and calculate the achieved condition number. | |
| """ | |
| assert n >= 6 | |
| n2 = n // 2 | |
| x = [0.0] * n | |
| y = [0.0] * n | |
| b = log2(c) | |
| # First half with exponents from 0 to |_b/2_| and random ints in between | |
| e = choices(range(int(b/2)), k=n2) | |
| e[0] = int(b / 2) + 1 | |
| e[-1] = 0.0 | |
| x[:n2] = [uniform(-1.0, 1.0) * exp2(p) for p in e] | |
| y[:n2] = [uniform(-1.0, 1.0) * exp2(p) for p in e] | |
| # Second half | |
| e = list(map(round, linspace(b/2, 0.0 , n-n2))) | |
| for i in range(n2, n): | |
| x[i] = uniform(-1.0, 1.0) * exp2(e[i - n2]) | |
| y[i] = (uniform(-1.0, 1.0) * exp2(e[i - n2]) - DotExact(x, y)) / x[i] | |
| # Shuffle | |
| pairs = list(zip(x, y)) | |
| shuffle(pairs) | |
| x, y = zip(*pairs) | |
| return DotExample(x, y, DotExact(x, y), Condition(x, y)) | |
| def RelativeError(res, ex): | |
| x, y, target_sumprod, condition = ex | |
| n = DotExact(list(x) + [-res], list(y) + [1]) | |
| return fabs(n / target_sumprod) | |
| def Trial(dotfunc, c, n): | |
| ex = GenDot(10, c) | |
| res = dotfunc(ex.x, ex.y) | |
| return RelativeError(res, ex) | |
| times = 1000 # Number of trials | |
| n = 20 # Length of vectors | |
| c = 1e30 # Target condition number | |
| # If the following test fails, it means that the C math library | |
| # implementation of fma() is not compliant with the C99 standard | |
| # and is inaccurate. To solve this problem, make a new build | |
| # with the symbol UNRELIABLE_FMA defined. That will enable a | |
| # slower but accurate code path that avoids the fma() call. | |
| relative_err = median(Trial(math.sumprod, c, n) for i in range(times)) | |
| self.assertLess(relative_err, 1e-16) | |
| def testModf(self): | |
| self.assertRaises(TypeError, math.modf) | |
| def testmodf(name, result, expected): | |
| (v1, v2), (e1, e2) = result, expected | |
| if abs(v1-e1) > eps or abs(v2-e2): | |
| self.fail('%s returned %r, expected %r'%\ | |
| (name, result, expected)) | |
| testmodf('modf(1.5)', math.modf(1.5), (0.5, 1.0)) | |
| testmodf('modf(-1.5)', math.modf(-1.5), (-0.5, -1.0)) | |
| self.assertEqual(math.modf(INF), (0.0, INF)) | |
| self.assertEqual(math.modf(NINF), (-0.0, NINF)) | |
| modf_nan = math.modf(NAN) | |
| self.assertTrue(math.isnan(modf_nan[0])) | |
| self.assertTrue(math.isnan(modf_nan[1])) | |
| def testPow(self): | |
| self.assertRaises(TypeError, math.pow) | |
| self.ftest('pow(0,1)', math.pow(0,1), 0) | |
| self.ftest('pow(1,0)', math.pow(1,0), 1) | |
| self.ftest('pow(2,1)', math.pow(2,1), 2) | |
| self.ftest('pow(2,-1)', math.pow(2,-1), 0.5) | |
| self.assertEqual(math.pow(INF, 1), INF) | |
| self.assertEqual(math.pow(NINF, 1), NINF) | |
| self.assertEqual((math.pow(1, INF)), 1.) | |
| self.assertEqual((math.pow(1, NINF)), 1.) | |
| self.assertTrue(math.isnan(math.pow(NAN, 1))) | |
| self.assertTrue(math.isnan(math.pow(2, NAN))) | |
| self.assertTrue(math.isnan(math.pow(0, NAN))) | |
| self.assertEqual(math.pow(1, NAN), 1) | |
| self.assertRaises(OverflowError, math.pow, 1e+100, 1e+100) | |
| # pow(0., x) | |
| self.assertEqual(math.pow(0., INF), 0.) | |
| self.assertEqual(math.pow(0., 3.), 0.) | |
| self.assertEqual(math.pow(0., 2.3), 0.) | |
| self.assertEqual(math.pow(0., 2.), 0.) | |
| self.assertEqual(math.pow(0., 0.), 1.) | |
| self.assertEqual(math.pow(0., -0.), 1.) | |
| self.assertRaises(ValueError, math.pow, 0., -2.) | |
| self.assertRaises(ValueError, math.pow, 0., -2.3) | |
| self.assertRaises(ValueError, math.pow, 0., -3.) | |
| self.assertEqual(math.pow(0., NINF), INF) | |
| self.assertTrue(math.isnan(math.pow(0., NAN))) | |
| # pow(INF, x) | |
| self.assertEqual(math.pow(INF, INF), INF) | |
| self.assertEqual(math.pow(INF, 3.), INF) | |
| self.assertEqual(math.pow(INF, 2.3), INF) | |
| self.assertEqual(math.pow(INF, 2.), INF) | |
| self.assertEqual(math.pow(INF, 0.), 1.) | |
| self.assertEqual(math.pow(INF, -0.), 1.) | |
| self.assertEqual(math.pow(INF, -2.), 0.) | |
| self.assertEqual(math.pow(INF, -2.3), 0.) | |
| self.assertEqual(math.pow(INF, -3.), 0.) | |
| self.assertEqual(math.pow(INF, NINF), 0.) | |
| self.assertTrue(math.isnan(math.pow(INF, NAN))) | |
| # pow(-0., x) | |
| self.assertEqual(math.pow(-0., INF), 0.) | |
| self.assertEqual(math.pow(-0., 3.), -0.) | |
| self.assertEqual(math.pow(-0., 2.3), 0.) | |
| self.assertEqual(math.pow(-0., 2.), 0.) | |
| self.assertEqual(math.pow(-0., 0.), 1.) | |
| self.assertEqual(math.pow(-0., -0.), 1.) | |
| self.assertRaises(ValueError, math.pow, -0., -2.) | |
| self.assertRaises(ValueError, math.pow, -0., -2.3) | |
| self.assertRaises(ValueError, math.pow, -0., -3.) | |
| self.assertEqual(math.pow(-0., NINF), INF) | |
| self.assertTrue(math.isnan(math.pow(-0., NAN))) | |
| # pow(NINF, x) | |
| self.assertEqual(math.pow(NINF, INF), INF) | |
| self.assertEqual(math.pow(NINF, 3.), NINF) | |
| self.assertEqual(math.pow(NINF, 2.3), INF) | |
| self.assertEqual(math.pow(NINF, 2.), INF) | |
| self.assertEqual(math.pow(NINF, 0.), 1.) | |
| self.assertEqual(math.pow(NINF, -0.), 1.) | |
| self.assertEqual(math.pow(NINF, -2.), 0.) | |
| self.assertEqual(math.pow(NINF, -2.3), 0.) | |
| self.assertEqual(math.pow(NINF, -3.), -0.) | |
| self.assertEqual(math.pow(NINF, NINF), 0.) | |
| self.assertTrue(math.isnan(math.pow(NINF, NAN))) | |
| # pow(-1, x) | |
| self.assertEqual(math.pow(-1., INF), 1.) | |
| self.assertEqual(math.pow(-1., 3.), -1.) | |
| self.assertRaises(ValueError, math.pow, -1., 2.3) | |
| self.assertEqual(math.pow(-1., 2.), 1.) | |
| self.assertEqual(math.pow(-1., 0.), 1.) | |
| self.assertEqual(math.pow(-1., -0.), 1.) | |
| self.assertEqual(math.pow(-1., -2.), 1.) | |
| self.assertRaises(ValueError, math.pow, -1., -2.3) | |
| self.assertEqual(math.pow(-1., -3.), -1.) | |
| self.assertEqual(math.pow(-1., NINF), 1.) | |
| self.assertTrue(math.isnan(math.pow(-1., NAN))) | |
| # pow(1, x) | |
| self.assertEqual(math.pow(1., INF), 1.) | |
| self.assertEqual(math.pow(1., 3.), 1.) | |
| self.assertEqual(math.pow(1., 2.3), 1.) | |
| self.assertEqual(math.pow(1., 2.), 1.) | |
| self.assertEqual(math.pow(1., 0.), 1.) | |
| self.assertEqual(math.pow(1., -0.), 1.) | |
| self.assertEqual(math.pow(1., -2.), 1.) | |
| self.assertEqual(math.pow(1., -2.3), 1.) | |
| self.assertEqual(math.pow(1., -3.), 1.) | |
| self.assertEqual(math.pow(1., NINF), 1.) | |
| self.assertEqual(math.pow(1., NAN), 1.) | |
| # pow(x, 0) should be 1 for any x | |
| self.assertEqual(math.pow(2.3, 0.), 1.) | |
| self.assertEqual(math.pow(-2.3, 0.), 1.) | |
| self.assertEqual(math.pow(NAN, 0.), 1.) | |
| self.assertEqual(math.pow(2.3, -0.), 1.) | |
| self.assertEqual(math.pow(-2.3, -0.), 1.) | |
| self.assertEqual(math.pow(NAN, -0.), 1.) | |
| # pow(x, y) is invalid if x is negative and y is not integral | |
| self.assertRaises(ValueError, math.pow, -1., 2.3) | |
| self.assertRaises(ValueError, math.pow, -15., -3.1) | |
| # pow(x, NINF) | |
| self.assertEqual(math.pow(1.9, NINF), 0.) | |
| self.assertEqual(math.pow(1.1, NINF), 0.) | |
| self.assertEqual(math.pow(0.9, NINF), INF) | |
| self.assertEqual(math.pow(0.1, NINF), INF) | |
| self.assertEqual(math.pow(-0.1, NINF), INF) | |
| self.assertEqual(math.pow(-0.9, NINF), INF) | |
| self.assertEqual(math.pow(-1.1, NINF), 0.) | |
| self.assertEqual(math.pow(-1.9, NINF), 0.) | |
| # pow(x, INF) | |
| self.assertEqual(math.pow(1.9, INF), INF) | |
| self.assertEqual(math.pow(1.1, INF), INF) | |
| self.assertEqual(math.pow(0.9, INF), 0.) | |
| self.assertEqual(math.pow(0.1, INF), 0.) | |
| self.assertEqual(math.pow(-0.1, INF), 0.) | |
| self.assertEqual(math.pow(-0.9, INF), 0.) | |
| self.assertEqual(math.pow(-1.1, INF), INF) | |
| self.assertEqual(math.pow(-1.9, INF), INF) | |
| # pow(x, y) should work for x negative, y an integer | |
| self.ftest('(-2.)**3.', math.pow(-2.0, 3.0), -8.0) | |
| self.ftest('(-2.)**2.', math.pow(-2.0, 2.0), 4.0) | |
| self.ftest('(-2.)**1.', math.pow(-2.0, 1.0), -2.0) | |
| self.ftest('(-2.)**0.', math.pow(-2.0, 0.0), 1.0) | |
| self.ftest('(-2.)**-0.', math.pow(-2.0, -0.0), 1.0) | |
| self.ftest('(-2.)**-1.', math.pow(-2.0, -1.0), -0.5) | |
| self.ftest('(-2.)**-2.', math.pow(-2.0, -2.0), 0.25) | |
| self.ftest('(-2.)**-3.', math.pow(-2.0, -3.0), -0.125) | |
| self.assertRaises(ValueError, math.pow, -2.0, -0.5) | |
| self.assertRaises(ValueError, math.pow, -2.0, 0.5) | |
| # the following tests have been commented out since they don't | |
| # really belong here: the implementation of ** for floats is | |
| # independent of the implementation of math.pow | |
| #self.assertEqual(1**NAN, 1) | |
| #self.assertEqual(1**INF, 1) | |
| #self.assertEqual(1**NINF, 1) | |
| #self.assertEqual(1**0, 1) | |
| #self.assertEqual(1.**NAN, 1) | |
| #self.assertEqual(1.**INF, 1) | |
| #self.assertEqual(1.**NINF, 1) | |
| #self.assertEqual(1.**0, 1) | |
| def testRadians(self): | |
| self.assertRaises(TypeError, math.radians) | |
| self.ftest('radians(180)', math.radians(180), math.pi) | |
| self.ftest('radians(90)', math.radians(90), math.pi/2) | |
| self.ftest('radians(-45)', math.radians(-45), -math.pi/4) | |
| self.ftest('radians(0)', math.radians(0), 0) | |
| @requires_IEEE_754 | |
| def testRemainder(self): | |
| from fractions import Fraction | |
| def validate_spec(x, y, r): | |
| """ | |
| Check that r matches remainder(x, y) according to the IEEE 754 | |
| specification. Assumes that x, y and r are finite and y is nonzero. | |
| """ | |
| fx, fy, fr = Fraction(x), Fraction(y), Fraction(r) | |
| # r should not exceed y/2 in absolute value | |
| self.assertLessEqual(abs(fr), abs(fy/2)) | |
| # x - r should be an exact integer multiple of y | |
| n = (fx - fr) / fy | |
| self.assertEqual(n, int(n)) | |
| if abs(fr) == abs(fy/2): | |
| # If |r| == |y/2|, n should be even. | |
| self.assertEqual(n/2, int(n/2)) | |
| # triples (x, y, remainder(x, y)) in hexadecimal form. | |
| testcases = [ | |
| # Remainders modulo 1, showing the ties-to-even behaviour. | |
| '-4.0 1 -0.0', | |
| '-3.8 1 0.8', | |
| '-3.0 1 -0.0', | |
| '-2.8 1 -0.8', | |
| '-2.0 1 -0.0', | |
| '-1.8 1 0.8', | |
| '-1.0 1 -0.0', | |
| '-0.8 1 -0.8', | |
| '-0.0 1 -0.0', | |
| ' 0.0 1 0.0', | |
| ' 0.8 1 0.8', | |
| ' 1.0 1 0.0', | |
| ' 1.8 1 -0.8', | |
| ' 2.0 1 0.0', | |
| ' 2.8 1 0.8', | |
| ' 3.0 1 0.0', | |
| ' 3.8 1 -0.8', | |
| ' 4.0 1 0.0', | |
| # Reductions modulo 2*pi | |
| '0x0.0p+0 0x1.921fb54442d18p+2 0x0.0p+0', | |
| '0x1.921fb54442d18p+0 0x1.921fb54442d18p+2 0x1.921fb54442d18p+0', | |
| '0x1.921fb54442d17p+1 0x1.921fb54442d18p+2 0x1.921fb54442d17p+1', | |
| '0x1.921fb54442d18p+1 0x1.921fb54442d18p+2 0x1.921fb54442d18p+1', | |
| '0x1.921fb54442d19p+1 0x1.921fb54442d18p+2 -0x1.921fb54442d17p+1', | |
| '0x1.921fb54442d17p+2 0x1.921fb54442d18p+2 -0x0.0000000000001p+2', | |
| '0x1.921fb54442d18p+2 0x1.921fb54442d18p+2 0x0p0', | |
| '0x1.921fb54442d19p+2 0x1.921fb54442d18p+2 0x0.0000000000001p+2', | |
| '0x1.2d97c7f3321d1p+3 0x1.921fb54442d18p+2 0x1.921fb54442d14p+1', | |
| '0x1.2d97c7f3321d2p+3 0x1.921fb54442d18p+2 -0x1.921fb54442d18p+1', | |
| '0x1.2d97c7f3321d3p+3 0x1.921fb54442d18p+2 -0x1.921fb54442d14p+1', | |
| '0x1.921fb54442d17p+3 0x1.921fb54442d18p+2 -0x0.0000000000001p+3', | |
| '0x1.921fb54442d18p+3 0x1.921fb54442d18p+2 0x0p0', | |
| '0x1.921fb54442d19p+3 0x1.921fb54442d18p+2 0x0.0000000000001p+3', | |
| '0x1.f6a7a2955385dp+3 0x1.921fb54442d18p+2 0x1.921fb54442d14p+1', | |
| '0x1.f6a7a2955385ep+3 0x1.921fb54442d18p+2 0x1.921fb54442d18p+1', | |
| '0x1.f6a7a2955385fp+3 0x1.921fb54442d18p+2 -0x1.921fb54442d14p+1', | |
| '0x1.1475cc9eedf00p+5 0x1.921fb54442d18p+2 0x1.921fb54442d10p+1', | |
| '0x1.1475cc9eedf01p+5 0x1.921fb54442d18p+2 -0x1.921fb54442d10p+1', | |
| # Symmetry with respect to signs. | |
| ' 1 0.c 0.4', | |
| '-1 0.c -0.4', | |
| ' 1 -0.c 0.4', | |
| '-1 -0.c -0.4', | |
| ' 1.4 0.c -0.4', | |
| '-1.4 0.c 0.4', | |
| ' 1.4 -0.c -0.4', | |
| '-1.4 -0.c 0.4', | |
| # Huge modulus, to check that the underlying algorithm doesn't | |
| # rely on 2.0 * modulus being representable. | |
| '0x1.dp+1023 0x1.4p+1023 0x0.9p+1023', | |
| '0x1.ep+1023 0x1.4p+1023 -0x0.ap+1023', | |
| '0x1.fp+1023 0x1.4p+1023 -0x0.9p+1023', | |
| ] | |
| for case in testcases: | |
| with self.subTest(case=case): | |
| x_hex, y_hex, expected_hex = case.split() | |
| x = float.fromhex(x_hex) | |
| y = float.fromhex(y_hex) | |
| expected = float.fromhex(expected_hex) | |
| validate_spec(x, y, expected) | |
| actual = math.remainder(x, y) | |
| # Cheap way of checking that the floats are | |
| # as identical as we need them to be. | |
| self.assertEqual(actual.hex(), expected.hex()) | |
| # Test tiny subnormal modulus: there's potential for | |
| # getting the implementation wrong here (for example, | |
| # by assuming that modulus/2 is exactly representable). | |
| tiny = float.fromhex('1p-1074') # min +ve subnormal | |
| for n in range(-25, 25): | |
| if n == 0: | |
| continue | |
| y = n * tiny | |
| for m in range(100): | |
| x = m * tiny | |
| actual = math.remainder(x, y) | |
| validate_spec(x, y, actual) | |
| actual = math.remainder(-x, y) | |
| validate_spec(-x, y, actual) | |
| # Special values. | |
| # NaNs should propagate as usual. | |
| for value in [NAN, 0.0, -0.0, 2.0, -2.3, NINF, INF]: | |
| self.assertIsNaN(math.remainder(NAN, value)) | |
| self.assertIsNaN(math.remainder(value, NAN)) | |
| # remainder(x, inf) is x, for non-nan non-infinite x. | |
| for value in [-2.3, -0.0, 0.0, 2.3]: | |
| self.assertEqual(math.remainder(value, INF), value) | |
| self.assertEqual(math.remainder(value, NINF), value) | |
| # remainder(x, 0) and remainder(infinity, x) for non-NaN x are invalid | |
| # operations according to IEEE 754-2008 7.2(f), and should raise. | |
| for value in [NINF, -2.3, -0.0, 0.0, 2.3, INF]: | |
| with self.assertRaises(ValueError): | |
| math.remainder(INF, value) | |
| with self.assertRaises(ValueError): | |
| math.remainder(NINF, value) | |
| with self.assertRaises(ValueError): | |
| math.remainder(value, 0.0) | |
| with self.assertRaises(ValueError): | |
| math.remainder(value, -0.0) | |
| def testSin(self): | |
| self.assertRaises(TypeError, math.sin) | |
| self.ftest('sin(0)', math.sin(0), 0) | |
| self.ftest('sin(pi/2)', math.sin(math.pi/2), 1) | |
| self.ftest('sin(-pi/2)', math.sin(-math.pi/2), -1) | |
| try: | |
| self.assertTrue(math.isnan(math.sin(INF))) | |
| self.assertTrue(math.isnan(math.sin(NINF))) | |
| except ValueError: | |
| self.assertRaises(ValueError, math.sin, INF) | |
| self.assertRaises(ValueError, math.sin, NINF) | |
| self.assertTrue(math.isnan(math.sin(NAN))) | |
| def testSinh(self): | |
| self.assertRaises(TypeError, math.sinh) | |
| self.ftest('sinh(0)', math.sinh(0), 0) | |
| self.ftest('sinh(1)**2-cosh(1)**2', math.sinh(1)**2-math.cosh(1)**2, -1) | |
| self.ftest('sinh(1)+sinh(-1)', math.sinh(1)+math.sinh(-1), 0) | |
| self.assertEqual(math.sinh(INF), INF) | |
| self.assertEqual(math.sinh(NINF), NINF) | |
| self.assertTrue(math.isnan(math.sinh(NAN))) | |
| def testSqrt(self): | |
| self.assertRaises(TypeError, math.sqrt) | |
| self.ftest('sqrt(0)', math.sqrt(0), 0) | |
| self.ftest('sqrt(0)', math.sqrt(0.0), 0.0) | |
| self.ftest('sqrt(2.5)', math.sqrt(2.5), 1.5811388300841898) | |
| self.ftest('sqrt(0.25)', math.sqrt(0.25), 0.5) | |
| self.ftest('sqrt(25.25)', math.sqrt(25.25), 5.024937810560445) | |
| self.ftest('sqrt(1)', math.sqrt(1), 1) | |
| self.ftest('sqrt(4)', math.sqrt(4), 2) | |
| self.assertEqual(math.sqrt(INF), INF) | |
| self.assertRaises(ValueError, math.sqrt, -1) | |
| self.assertRaises(ValueError, math.sqrt, NINF) | |
| self.assertTrue(math.isnan(math.sqrt(NAN))) | |
| def testTan(self): | |
| self.assertRaises(TypeError, math.tan) | |
| self.ftest('tan(0)', math.tan(0), 0) | |
| self.ftest('tan(pi/4)', math.tan(math.pi/4), 1) | |
| self.ftest('tan(-pi/4)', math.tan(-math.pi/4), -1) | |
| try: | |
| self.assertTrue(math.isnan(math.tan(INF))) | |
| self.assertTrue(math.isnan(math.tan(NINF))) | |
| except ValueError: | |
| self.assertRaises(ValueError, math.tan, INF) | |
| self.assertRaises(ValueError, math.tan, NINF) | |
| self.assertTrue(math.isnan(math.tan(NAN))) | |
| def testTanh(self): | |
| self.assertRaises(TypeError, math.tanh) | |
| self.ftest('tanh(0)', math.tanh(0), 0) | |
| self.ftest('tanh(1)+tanh(-1)', math.tanh(1)+math.tanh(-1), 0, | |
| abs_tol=math.ulp(1)) | |
| self.ftest('tanh(inf)', math.tanh(INF), 1) | |
| self.ftest('tanh(-inf)', math.tanh(NINF), -1) | |
| self.assertTrue(math.isnan(math.tanh(NAN))) | |
| @requires_IEEE_754 | |
| def testTanhSign(self): | |
| # check that tanh(-0.) == -0. on IEEE 754 systems | |
| self.assertEqual(math.tanh(-0.), -0.) | |
| self.assertEqual(math.copysign(1., math.tanh(-0.)), | |
| math.copysign(1., -0.)) | |
| def test_trunc(self): | |
| self.assertEqual(math.trunc(1), 1) | |
| self.assertEqual(math.trunc(-1), -1) | |
| self.assertEqual(type(math.trunc(1)), int) | |
| self.assertEqual(type(math.trunc(1.5)), int) | |
| self.assertEqual(math.trunc(1.5), 1) | |
| self.assertEqual(math.trunc(-1.5), -1) | |
| self.assertEqual(math.trunc(1.999999), 1) | |
| self.assertEqual(math.trunc(-1.999999), -1) | |
| self.assertEqual(math.trunc(-0.999999), -0) | |
| self.assertEqual(math.trunc(-100.999), -100) | |
| class TestTrunc: | |
| def __trunc__(self): | |
| return 23 | |
| class FloatTrunc(float): | |
| def __trunc__(self): | |
| return 23 | |
| class TestNoTrunc: | |
| pass | |
| class TestBadTrunc: | |
| __trunc__ = BadDescr() | |
| self.assertEqual(math.trunc(TestTrunc()), 23) | |
| self.assertEqual(math.trunc(FloatTrunc()), 23) | |
| self.assertRaises(TypeError, math.trunc) | |
| self.assertRaises(TypeError, math.trunc, 1, 2) | |
| self.assertRaises(TypeError, math.trunc, FloatLike(23.5)) | |
| self.assertRaises(TypeError, math.trunc, TestNoTrunc()) | |
| self.assertRaises(ValueError, math.trunc, TestBadTrunc()) | |
| def testIsfinite(self): | |
| self.assertTrue(math.isfinite(0.0)) | |
| self.assertTrue(math.isfinite(-0.0)) | |
| self.assertTrue(math.isfinite(1.0)) | |
| self.assertTrue(math.isfinite(-1.0)) | |
| self.assertFalse(math.isfinite(float("nan"))) | |
| self.assertFalse(math.isfinite(float("inf"))) | |
| self.assertFalse(math.isfinite(float("-inf"))) | |
| def testIsnormal(self): | |
| self.assertTrue(math.isnormal(1.25)) | |
| self.assertTrue(math.isnormal(-1.0)) | |
| self.assertFalse(math.isnormal(0.0)) | |
| self.assertFalse(math.isnormal(-0.0)) | |
| self.assertFalse(math.isnormal(INF)) | |
| self.assertFalse(math.isnormal(NINF)) | |
| self.assertFalse(math.isnormal(NAN)) | |
| self.assertFalse(math.isnormal(FLOAT_MIN/2)) | |
| self.assertFalse(math.isnormal(-FLOAT_MIN/2)) | |
| def testIssubnormal(self): | |
| self.assertFalse(math.issubnormal(1.25)) | |
| self.assertFalse(math.issubnormal(-1.0)) | |
| self.assertFalse(math.issubnormal(0.0)) | |
| self.assertFalse(math.issubnormal(-0.0)) | |
| self.assertFalse(math.issubnormal(INF)) | |
| self.assertFalse(math.issubnormal(NINF)) | |
| self.assertFalse(math.issubnormal(NAN)) | |
| self.assertTrue(math.issubnormal(FLOAT_MIN/2)) | |
| self.assertTrue(math.issubnormal(-FLOAT_MIN/2)) | |
| def testIsnan(self): | |
| self.assertTrue(math.isnan(float("nan"))) | |
| self.assertTrue(math.isnan(float("-nan"))) | |
| self.assertTrue(math.isnan(float("inf") * 0.)) | |
| self.assertFalse(math.isnan(float("inf"))) | |
| self.assertFalse(math.isnan(0.)) | |
| self.assertFalse(math.isnan(1.)) | |
| def testIsinf(self): | |
| self.assertTrue(math.isinf(float("inf"))) | |
| self.assertTrue(math.isinf(float("-inf"))) | |
| self.assertTrue(math.isinf(1E400)) | |
| self.assertTrue(math.isinf(-1E400)) | |
| self.assertFalse(math.isinf(float("nan"))) | |
| self.assertFalse(math.isinf(0.)) | |
| self.assertFalse(math.isinf(1.)) | |
| def test_nan_constant(self): | |
| # `math.nan` must be a quiet NaN with positive sign bit | |
| self.assertTrue(math.isnan(math.nan)) | |
| self.assertEqual(math.copysign(1., math.nan), 1.) | |
| def test_inf_constant(self): | |
| self.assertTrue(math.isinf(math.inf)) | |
| self.assertGreater(math.inf, 0.0) | |
| self.assertEqual(math.inf, float("inf")) | |
| self.assertEqual(-math.inf, float("-inf")) | |
| # RED_FLAG 16-Oct-2000 Tim | |
| # While 2.0 is more consistent about exceptions than previous releases, it | |
| # still fails this part of the test on some platforms. For now, we only | |
| # *run* test_exceptions() in verbose mode, so that this isn't normally | |
| # tested. | |
| @unittest.skipUnless(verbose, 'requires verbose mode') | |
| def test_exceptions(self): | |
| try: | |
| x = math.exp(-1000000000) | |
| except: | |
| # mathmodule.c is failing to weed out underflows from libm, or | |
| # we've got an fp format with huge dynamic range | |
| self.fail("underflowing exp() should not have raised " | |
| "an exception") | |
| if x != 0: | |
| self.fail("underflowing exp() should have returned 0") | |
| # If this fails, probably using a strict IEEE-754 conforming libm, and x | |
| # is +Inf afterwards. But Python wants overflows detected by default. | |
| try: | |
| x = math.exp(1000000000) | |
| except OverflowError: | |
| pass | |
| else: | |
| self.fail("overflowing exp() didn't trigger OverflowError") | |
| # If this fails, it could be a puzzle. One odd possibility is that | |
| # mathmodule.c's macros are getting confused while comparing | |
| # Inf (HUGE_VAL) to a NaN, and artificially setting errno to ERANGE | |
| # as a result (and so raising OverflowError instead). | |
| try: | |
| x = math.sqrt(-1.0) | |
| except ValueError: | |
| pass | |
| else: | |
| self.fail("sqrt(-1) didn't raise ValueError") | |
| @requires_IEEE_754 | |
| def test_testfile(self): | |
| # Some tests need to be skipped on ancient OS X versions. | |
| # See issue #27953. | |
| SKIP_ON_TIGER = {'tan0064'} | |
| osx_version = None | |
| if sys.platform == 'darwin': | |
| version_txt = platform.mac_ver()[0] | |
| try: | |
| osx_version = tuple(map(int, version_txt.split('.'))) | |
| except ValueError: | |
| pass | |
| fail_fmt = "{}: {}({!r}): {}" | |
| failures = [] | |
| for id, fn, ar, ai, er, ei, flags in parse_testfile(test_file): | |
| # Skip if either the input or result is complex | |
| if ai != 0.0 or ei != 0.0: | |
| continue | |
| if fn in ['rect', 'polar']: | |
| # no real versions of rect, polar | |
| continue | |
| # Skip certain tests on OS X 10.4. | |
| if osx_version is not None and osx_version < (10, 5): | |
| if id in SKIP_ON_TIGER: | |
| continue | |
| func = getattr(math, fn) | |
| if 'invalid' in flags or 'divide-by-zero' in flags: | |
| er = 'ValueError' | |
| elif 'overflow' in flags: | |
| er = 'OverflowError' | |
| try: | |
| result = func(ar) | |
| except ValueError: | |
| result = 'ValueError' | |
| except OverflowError: | |
| result = 'OverflowError' | |
| # C99+ says for math.h's sqrt: If the argument is +∞ or ±0, it is | |
| # returned, unmodified. On another hand, for csqrt: If z is ±0+0i, | |
| # the result is +0+0i. Lets correct zero sign of er to follow | |
| # first convention. | |
| if id in ['sqrt0002', 'sqrt0003', 'sqrt1001', 'sqrt1023']: | |
| er = math.copysign(er, ar) | |
| # Default tolerances | |
| ulp_tol, abs_tol = 5, 0.0 | |
| failure = result_check(er, result, ulp_tol, abs_tol) | |
| if failure is None: | |
| continue | |
| msg = fail_fmt.format(id, fn, ar, failure) | |
| failures.append(msg) | |
| if failures: | |
| self.fail('Failures in test_testfile:\n ' + | |
| '\n '.join(failures)) | |
| @requires_IEEE_754 | |
| def test_mtestfile(self): | |
| fail_fmt = "{}: {}{!r}: {}" | |
| failures = [] | |
| ids = set() | |
| for id, fn, args, expected, flags in parse_mtestfile(math_testcases): | |
| if id in ids: | |
| failures.append(f"Duplicate test id {id}") | |
| ids.add(id) | |
| func = getattr(math, fn) | |
| if 'invalid' in flags or 'divide-by-zero' in flags: | |
| expected = 'ValueError' | |
| elif 'overflow' in flags: | |
| expected = 'OverflowError' | |
| try: | |
| got = func(*args) | |
| except ValueError: | |
| got = 'ValueError' | |
| except OverflowError: | |
| got = 'OverflowError' | |
| # Default tolerances | |
| ulp_tol, abs_tol = 5, 0.0 | |
| # Exceptions to the defaults | |
| if fn == 'gamma': | |
| # Experimental results on one platform gave | |
| # an accuracy of <= 10 ulps across the entire float | |
| # domain. We weaken that to require 20 ulp accuracy. | |
| ulp_tol = 20 | |
| elif fn == 'lgamma': | |
| # we use a weaker accuracy test for lgamma; | |
| # lgamma only achieves an absolute error of | |
| # a few multiples of the machine accuracy, in | |
| # general. | |
| abs_tol = 1e-15 | |
| elif fn == 'erfc' and (arg := args[0]) >= 0.0: | |
| # erfc has less-than-ideal accuracy for large | |
| # arguments (x ~ 25 or so), mainly due to the | |
| # error involved in computing exp(-x*x). | |
| # | |
| # Observed between CPython and mpmath at 25 dp: | |
| # x < 0 : err <= 2 ulp | |
| # 0 <= x < 1 : err <= 10 ulp | |
| # 1 <= x < 10 : err <= 100 ulp | |
| # 10 <= x < 20 : err <= 300 ulp | |
| # 20 <= x : < 600 ulp | |
| # | |
| if arg < 1.0: | |
| ulp_tol = 10 | |
| elif arg < 10.0: | |
| ulp_tol = 100 | |
| else: | |
| ulp_tol = 1000 | |
| failure = result_check(expected, got, ulp_tol, abs_tol) | |
| if failure is None: | |
| continue | |
| msg = fail_fmt.format(id, fn, args, failure) | |
| failures.append(msg) | |
| if failures: | |
| self.fail('Failures in test_mtestfile:\n ' + | |
| '\n '.join(failures)) | |
| def test_prod(self): | |
| from fractions import Fraction as F | |
| prod = math.prod | |
| self.assertEqual(prod([]), 1) | |
| self.assertEqual(prod([], start=5), 5) | |
| self.assertEqual(prod(list(range(2,8))), 5040) | |
| self.assertEqual(prod(iter(list(range(2,8)))), 5040) | |
| self.assertEqual(prod(range(1, 10), start=10), 3628800) | |
| self.assertEqual(prod([1, 2, 3, 4, 5]), 120) | |
| self.assertEqual(prod([1.0, 2.0, 3.0, 4.0, 5.0]), 120.0) | |
| self.assertEqual(prod([1, 2, 3, 4.0, 5.0]), 120.0) | |
| self.assertEqual(prod([1.0, 2.0, 3.0, 4, 5]), 120.0) | |
| self.assertEqual(prod([1., F(3, 2)]), 1.5) | |
| # Error in multiplication | |
| class BadMultiply: | |
| def __rmul__(self, other): | |
| raise RuntimeError | |
| with self.assertRaises(RuntimeError): | |
| prod([10., BadMultiply()]) | |
| # Test overflow in fast-path for integers | |
| self.assertEqual(prod([1, 1, 2**32, 1, 1]), 2**32) | |
| # Test overflow in fast-path for floats | |
| self.assertEqual(prod([1.0, 1.0, 2**32, 1, 1]), float(2**32)) | |
| self.assertRaises(TypeError, prod) | |
| self.assertRaises(TypeError, prod, 42) | |
| self.assertRaises(TypeError, prod, ['a', 'b', 'c']) | |
| self.assertRaises(TypeError, prod, ['a', 'b', 'c'], start='') | |
| self.assertRaises(TypeError, prod, [b'a', b'c'], start=b'') | |
| values = [bytearray(b'a'), bytearray(b'b')] | |
| self.assertRaises(TypeError, prod, values, start=bytearray(b'')) | |
| self.assertRaises(TypeError, prod, [[1], [2], [3]]) | |
| self.assertRaises(TypeError, prod, [{2:3}]) | |
| self.assertRaises(TypeError, prod, [{2:3}]*2, start={2:3}) | |
| self.assertRaises(TypeError, prod, [[1], [2], [3]], start=[]) | |
| # Some odd cases | |
| self.assertEqual(prod([2, 3], start='ab'), 'abababababab') | |
| self.assertEqual(prod([2, 3], start=[1, 2]), [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2]) | |
| self.assertEqual(prod([], start={2: 3}), {2:3}) | |
| with self.assertRaises(TypeError): | |
| prod([10, 20], 1) # start is a keyword-only argument | |
| self.assertEqual(prod([0, 1, 2, 3]), 0) | |
| self.assertEqual(prod([1, 0, 2, 3]), 0) | |
| self.assertEqual(prod([1, 2, 3, 0]), 0) | |
| def _naive_prod(iterable, start=1): | |
| for elem in iterable: | |
| start *= elem | |
| return start | |
| # Big integers | |
| iterable = range(1, 10000) | |
| self.assertEqual(prod(iterable), _naive_prod(iterable)) | |
| iterable = range(-10000, -1) | |
| self.assertEqual(prod(iterable), _naive_prod(iterable)) | |
| iterable = range(-1000, 1000) | |
| self.assertEqual(prod(iterable), 0) | |
| # Big floats | |
| iterable = [float(x) for x in range(1, 1000)] | |
| self.assertEqual(prod(iterable), _naive_prod(iterable)) | |
| iterable = [float(x) for x in range(-1000, -1)] | |
| self.assertEqual(prod(iterable), _naive_prod(iterable)) | |
| iterable = [float(x) for x in range(-1000, 1000)] | |
| self.assertIsNaN(prod(iterable)) | |
| # Float tests | |
| self.assertIsNaN(prod([1, 2, 3, float("nan"), 2, 3])) | |
| self.assertIsNaN(prod([1, 0, float("nan"), 2, 3])) | |
| self.assertIsNaN(prod([1, float("nan"), 0, 3])) | |
| self.assertIsNaN(prod([1, float("inf"), float("nan"),3])) | |
| self.assertIsNaN(prod([1, float("-inf"), float("nan"),3])) | |
| self.assertIsNaN(prod([1, float("nan"), float("inf"),3])) | |
| self.assertIsNaN(prod([1, float("nan"), float("-inf"),3])) | |
| self.assertEqual(prod([1, 2, 3, float('inf'),-3,4]), float('-inf')) | |
| self.assertEqual(prod([1, 2, 3, float('-inf'),-3,4]), float('inf')) | |
| self.assertIsNaN(prod([1,2,0,float('inf'), -3, 4])) | |
| self.assertIsNaN(prod([1,2,0,float('-inf'), -3, 4])) | |
| self.assertIsNaN(prod([1, 2, 3, float('inf'), -3, 0, 3])) | |
| self.assertIsNaN(prod([1, 2, 3, float('-inf'), -3, 0, 2])) | |
| # Type preservation | |
| self.assertEqual(type(prod([1, 2, 3, 4, 5, 6])), int) | |
| self.assertEqual(type(prod([1, 2.0, 3, 4, 5, 6])), float) | |
| self.assertEqual(type(prod(range(1, 10000))), int) | |
| self.assertEqual(type(prod(range(1, 10000), start=1.0)), float) | |
| self.assertEqual(type(prod([1, decimal.Decimal(2.0), 3, 4, 5, 6])), | |
| decimal.Decimal) | |
| @requires_IEEE_754 | |
| def test_nextafter(self): | |
| # around 2^52 and 2^63 | |
| self.assertEqual(math.nextafter(4503599627370496.0, -INF), | |
| 4503599627370495.5) | |
| self.assertEqual(math.nextafter(4503599627370496.0, INF), | |
| 4503599627370497.0) | |
| self.assertEqual(math.nextafter(9223372036854775808.0, 0.0), | |
| 9223372036854774784.0) | |
| self.assertEqual(math.nextafter(-9223372036854775808.0, 0.0), | |
| -9223372036854774784.0) | |
| # around 1.0 | |
| self.assertEqual(math.nextafter(1.0, -INF), | |
| float.fromhex('0x1.fffffffffffffp-1')) | |
| self.assertEqual(math.nextafter(1.0, INF), | |
| float.fromhex('0x1.0000000000001p+0')) | |
| self.assertEqual(math.nextafter(1.0, -INF, steps=1), | |
| float.fromhex('0x1.fffffffffffffp-1')) | |
| self.assertEqual(math.nextafter(1.0, INF, steps=1), | |
| float.fromhex('0x1.0000000000001p+0')) | |
| self.assertEqual(math.nextafter(1.0, -INF, steps=3), | |
| float.fromhex('0x1.ffffffffffffdp-1')) | |
| self.assertEqual(math.nextafter(1.0, INF, steps=3), | |
| float.fromhex('0x1.0000000000003p+0')) | |
| # x == y: y is returned | |
| for steps in range(1, 5): | |
| self.assertEqual(math.nextafter(2.0, 2.0, steps=steps), 2.0) | |
| self.assertEqualSign(math.nextafter(-0.0, +0.0, steps=steps), +0.0) | |
| self.assertEqualSign(math.nextafter(+0.0, -0.0, steps=steps), -0.0) | |
| # around 0.0 | |
| smallest_subnormal = sys.float_info.min * sys.float_info.epsilon | |
| self.assertEqual(math.nextafter(+0.0, INF), smallest_subnormal) | |
| self.assertEqual(math.nextafter(-0.0, INF), smallest_subnormal) | |
| self.assertEqual(math.nextafter(+0.0, -INF), -smallest_subnormal) | |
| self.assertEqual(math.nextafter(-0.0, -INF), -smallest_subnormal) | |
| self.assertEqualSign(math.nextafter(smallest_subnormal, +0.0), +0.0) | |
| self.assertEqualSign(math.nextafter(-smallest_subnormal, +0.0), -0.0) | |
| self.assertEqualSign(math.nextafter(smallest_subnormal, -0.0), +0.0) | |
| self.assertEqualSign(math.nextafter(-smallest_subnormal, -0.0), -0.0) | |
| # around infinity | |
| largest_normal = sys.float_info.max | |
| self.assertEqual(math.nextafter(INF, 0.0), largest_normal) | |
| self.assertEqual(math.nextafter(-INF, 0.0), -largest_normal) | |
| self.assertEqual(math.nextafter(largest_normal, INF), INF) | |
| self.assertEqual(math.nextafter(-largest_normal, -INF), -INF) | |
| # NaN | |
| self.assertIsNaN(math.nextafter(NAN, 1.0)) | |
| self.assertIsNaN(math.nextafter(1.0, NAN)) | |
| self.assertIsNaN(math.nextafter(NAN, NAN)) | |
| self.assertEqual(1.0, math.nextafter(1.0, INF, steps=0)) | |
| with self.assertRaises(ValueError): | |
| math.nextafter(1.0, INF, steps=-1) | |
| @requires_IEEE_754 | |
| def test_ulp(self): | |
| self.assertEqual(math.ulp(1.0), sys.float_info.epsilon) | |
| # use int ** int rather than float ** int to not rely on pow() accuracy | |
| self.assertEqual(math.ulp(2 ** 52), 1.0) | |
| self.assertEqual(math.ulp(2 ** 53), 2.0) | |
| self.assertEqual(math.ulp(2 ** 64), 4096.0) | |
| # min and max | |
| self.assertEqual(math.ulp(0.0), | |
| sys.float_info.min * sys.float_info.epsilon) | |
| self.assertEqual(math.ulp(FLOAT_MAX), | |
| FLOAT_MAX - math.nextafter(FLOAT_MAX, -INF)) | |
| # special cases | |
| self.assertEqual(math.ulp(INF), INF) | |
| self.assertIsNaN(math.ulp(math.nan)) | |
| # negative number: ulp(-x) == ulp(x) | |
| for x in (0.0, 1.0, 2 ** 52, 2 ** 64, INF): | |
| with self.subTest(x=x): | |
| self.assertEqual(math.ulp(-x), math.ulp(x)) | |
| def test_issue39871(self): | |
| # A SystemError should not be raised if the first arg to atan2(), | |
| # copysign(), or remainder() cannot be converted to a float. | |
| class F: | |
| def __float__(self): | |
| self.converted = True | |
| 1/0 | |
| for func in math.atan2, math.copysign, math.remainder: | |
| y = F() | |
| with self.assertRaises(TypeError): | |
| func("not a number", y) | |
| # There should not have been any attempt to convert the second | |
| # argument to a float. | |
| self.assertFalse(getattr(y, "converted", False)) | |
| def test_input_exceptions(self): | |
| self.assertRaises(TypeError, math.exp, "spam") | |
| self.assertRaises(TypeError, math.erf, "spam") | |
| self.assertRaises(TypeError, math.atan2, "spam", 1.0) | |
| self.assertRaises(TypeError, math.atan2, 1.0, "spam") | |
| self.assertRaises(TypeError, math.atan2, 1.0) | |
| self.assertRaises(TypeError, math.atan2, 1.0, 2.0, 3.0) | |
| def test_exception_messages(self): | |
| x = -1.1 | |
| with self.assertRaisesRegex(ValueError, | |
| f"expected a nonnegative input, got {x}"): | |
| math.sqrt(x) | |
| with self.assertRaisesRegex(ValueError, | |
| f"expected a positive input, got {x}"): | |
| math.log(x) | |
| with self.assertRaisesRegex(ValueError, | |
| f"expected a positive input, got {x}"): | |
| math.log(123, x) | |
| with self.assertRaisesRegex(ValueError, | |
| f"expected a positive input, got {x}"): | |
| math.log(x, 123) | |
| with self.assertRaisesRegex(ValueError, | |
| f"expected a positive input, got {x}"): | |
| math.log2(x) | |
| with self.assertRaisesRegex(ValueError, | |
| f"expected a positive input, got {x}"): | |
| math.log10(x) | |
| x = decimal.Decimal('-1.1') | |
| with self.assertRaisesRegex(ValueError, | |
| f"expected a positive input, got {x}"): | |
| math.log(x) | |
| x = fractions.Fraction(1, 10**400) | |
| with self.assertRaisesRegex(ValueError, | |
| f"expected a positive input, got {float(x)}"): | |
| math.log(x) | |
| x = -123 | |
| with self.assertRaisesRegex(ValueError, | |
| "expected a positive input$"): | |
| math.log(x) | |
| with self.assertRaisesRegex(ValueError, | |
| f"expected a noninteger or positive integer, got {x}"): | |
| math.gamma(x) | |
| x = 1.0 | |
| with self.assertRaisesRegex(ValueError, | |
| f"expected a number between -1 and 1, got {x}"): | |
| math.atanh(x) | |
| # Custom assertions. | |
| def assertIsNaN(self, value): | |
| if not math.isnan(value): | |
| self.fail("Expected a NaN, got {!r}.".format(value)) | |
| def assertEqualSign(self, x, y): | |
| """Similar to assertEqual(), but compare also the sign with copysign(). | |
| Function useful to compare signed zeros. | |
| """ | |
| self.assertEqual(x, y) | |
| self.assertEqual(math.copysign(1.0, x), math.copysign(1.0, y)) | |
| class IsCloseTests(unittest.TestCase): | |
| isclose = math.isclose # subclasses 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_asymmetry(self): | |
| # test the asymmetry 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')), | |
| (Decimal('1.00000001e20'), Decimal('1.0e20'))] | |
| 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 | |
| fraction_examples = [ | |
| (Fraction(1, 100000000) + 1, Fraction(1)), | |
| (Fraction(100000001), Fraction(100000000)), | |
| (Fraction(10**8 + 1, 10**28), Fraction(1, 10**20))] | |
| self.assertAllClose(fraction_examples, rel_tol=1e-8) | |
| self.assertAllNotClose(fraction_examples, rel_tol=1e-9) | |
| class FMATests(unittest.TestCase): | |
| """ Tests for math.fma. """ | |
| def test_fma_nan_results(self): | |
| # Selected representative values. | |
| values = [ | |
| -math.inf, -1e300, -2.3, -1e-300, -0.0, | |
| 0.0, 1e-300, 2.3, 1e300, math.inf, math.nan | |
| ] | |
| # If any input is a NaN, the result should be a NaN, too. | |
| for a, b in itertools.product(values, repeat=2): | |
| with self.subTest(a=a, b=b): | |
| self.assertIsNaN(math.fma(math.nan, a, b)) | |
| self.assertIsNaN(math.fma(a, math.nan, b)) | |
| self.assertIsNaN(math.fma(a, b, math.nan)) | |
| @support.skip_on_newlib | |
| def test_fma_infinities(self): | |
| # Cases involving infinite inputs or results. | |
| positives = [1e-300, 2.3, 1e300, math.inf] | |
| finites = [-1e300, -2.3, -1e-300, -0.0, 0.0, 1e-300, 2.3, 1e300] | |
| non_nans = [-math.inf, -2.3, -0.0, 0.0, 2.3, math.inf] | |
| # ValueError due to inf * 0 computation. | |
| for c in non_nans: | |
| for infinity in [math.inf, -math.inf]: | |
| for zero in [0.0, -0.0]: | |
| with self.subTest(c=c, infinity=infinity, zero=zero): | |
| with self.assertRaises(ValueError): | |
| math.fma(infinity, zero, c) | |
| with self.assertRaises(ValueError): | |
| math.fma(zero, infinity, c) | |
| # ValueError when a*b and c both infinite of opposite signs. | |
| for b in positives: | |
| with self.subTest(b=b): | |
| with self.assertRaises(ValueError): | |
| math.fma(math.inf, b, -math.inf) | |
| with self.assertRaises(ValueError): | |
| math.fma(math.inf, -b, math.inf) | |
| with self.assertRaises(ValueError): | |
| math.fma(-math.inf, -b, -math.inf) | |
| with self.assertRaises(ValueError): | |
| math.fma(-math.inf, b, math.inf) | |
| with self.assertRaises(ValueError): | |
| math.fma(b, math.inf, -math.inf) | |
| with self.assertRaises(ValueError): | |
| math.fma(-b, math.inf, math.inf) | |
| with self.assertRaises(ValueError): | |
| math.fma(-b, -math.inf, -math.inf) | |
| with self.assertRaises(ValueError): | |
| math.fma(b, -math.inf, math.inf) | |
| # Infinite result when a*b and c both infinite of the same sign. | |
| for b in positives: | |
| with self.subTest(b=b): | |
| self.assertEqual(math.fma(math.inf, b, math.inf), math.inf) | |
| self.assertEqual(math.fma(math.inf, -b, -math.inf), -math.inf) | |
| self.assertEqual(math.fma(-math.inf, -b, math.inf), math.inf) | |
| self.assertEqual(math.fma(-math.inf, b, -math.inf), -math.inf) | |
| self.assertEqual(math.fma(b, math.inf, math.inf), math.inf) | |
| self.assertEqual(math.fma(-b, math.inf, -math.inf), -math.inf) | |
| self.assertEqual(math.fma(-b, -math.inf, math.inf), math.inf) | |
| self.assertEqual(math.fma(b, -math.inf, -math.inf), -math.inf) | |
| # Infinite result when a*b finite, c infinite. | |
| for a, b in itertools.product(finites, finites): | |
| with self.subTest(b=b): | |
| self.assertEqual(math.fma(a, b, math.inf), math.inf) | |
| self.assertEqual(math.fma(a, b, -math.inf), -math.inf) | |
| # Infinite result when a*b infinite, c finite. | |
| for b, c in itertools.product(positives, finites): | |
| with self.subTest(b=b, c=c): | |
| self.assertEqual(math.fma(math.inf, b, c), math.inf) | |
| self.assertEqual(math.fma(-math.inf, b, c), -math.inf) | |
| self.assertEqual(math.fma(-math.inf, -b, c), math.inf) | |
| self.assertEqual(math.fma(math.inf, -b, c), -math.inf) | |
| self.assertEqual(math.fma(b, math.inf, c), math.inf) | |
| self.assertEqual(math.fma(b, -math.inf, c), -math.inf) | |
| self.assertEqual(math.fma(-b, -math.inf, c), math.inf) | |
| self.assertEqual(math.fma(-b, math.inf, c), -math.inf) | |
| # gh-73468: On some platforms, libc fma() doesn't implement IEE 754-2008 | |
| # properly: it doesn't use the right sign when the result is zero. | |
| @unittest.skipIf( | |
| sys.platform.startswith(("freebsd", "wasi", "netbsd", "emscripten", "cygwin")) | |
| or (sys.platform == "android" and platform.machine() == "x86_64") | |
| or (support.linked_to_musl() and support.linked_to_musl() < (1, 2, 6)), # gh-131032 | |
| f"this platform doesn't implement IEE 754-2008 properly") | |
| def test_fma_zero_result(self): | |
| nonnegative_finites = [0.0, 1e-300, 2.3, 1e300] | |
| # Zero results from exact zero inputs. | |
| for b in nonnegative_finites: | |
| with self.subTest(b=b): | |
| self.assertIsPositiveZero(math.fma(0.0, b, 0.0)) | |
| self.assertIsPositiveZero(math.fma(0.0, b, -0.0)) | |
| self.assertIsNegativeZero(math.fma(0.0, -b, -0.0)) | |
| self.assertIsPositiveZero(math.fma(0.0, -b, 0.0)) | |
| self.assertIsPositiveZero(math.fma(-0.0, -b, 0.0)) | |
| self.assertIsPositiveZero(math.fma(-0.0, -b, -0.0)) | |
| self.assertIsNegativeZero(math.fma(-0.0, b, -0.0)) | |
| self.assertIsPositiveZero(math.fma(-0.0, b, 0.0)) | |
| self.assertIsPositiveZero(math.fma(b, 0.0, 0.0)) | |
| self.assertIsPositiveZero(math.fma(b, 0.0, -0.0)) | |
| self.assertIsNegativeZero(math.fma(-b, 0.0, -0.0)) | |
| self.assertIsPositiveZero(math.fma(-b, 0.0, 0.0)) | |
| self.assertIsPositiveZero(math.fma(-b, -0.0, 0.0)) | |
| self.assertIsPositiveZero(math.fma(-b, -0.0, -0.0)) | |
| self.assertIsNegativeZero(math.fma(b, -0.0, -0.0)) | |
| self.assertIsPositiveZero(math.fma(b, -0.0, 0.0)) | |
| # Exact zero result from nonzero inputs. | |
| self.assertIsPositiveZero(math.fma(2.0, 2.0, -4.0)) | |
| self.assertIsPositiveZero(math.fma(2.0, -2.0, 4.0)) | |
| self.assertIsPositiveZero(math.fma(-2.0, -2.0, -4.0)) | |
| self.assertIsPositiveZero(math.fma(-2.0, 2.0, 4.0)) | |
| # Underflow to zero. | |
| tiny = 1e-300 | |
| self.assertIsPositiveZero(math.fma(tiny, tiny, 0.0)) | |
| self.assertIsNegativeZero(math.fma(tiny, -tiny, 0.0)) | |
| self.assertIsPositiveZero(math.fma(-tiny, -tiny, 0.0)) | |
| self.assertIsNegativeZero(math.fma(-tiny, tiny, 0.0)) | |
| self.assertIsPositiveZero(math.fma(tiny, tiny, -0.0)) | |
| self.assertIsNegativeZero(math.fma(tiny, -tiny, -0.0)) | |
| self.assertIsPositiveZero(math.fma(-tiny, -tiny, -0.0)) | |
| self.assertIsNegativeZero(math.fma(-tiny, tiny, -0.0)) | |
| # Corner case where rounding the multiplication would | |
| # give the wrong result. | |
| x = float.fromhex('0x1p-500') | |
| y = float.fromhex('0x1p-550') | |
| z = float.fromhex('0x1p-1000') | |
| self.assertIsNegativeZero(math.fma(x-y, x+y, -z)) | |
| self.assertIsPositiveZero(math.fma(y-x, x+y, z)) | |
| self.assertIsNegativeZero(math.fma(y-x, -(x+y), -z)) | |
| self.assertIsPositiveZero(math.fma(x-y, -(x+y), z)) | |
| @support.skip_on_newlib | |
| def test_fma_overflow(self): | |
| a = b = float.fromhex('0x1p512') | |
| c = float.fromhex('0x1p1023') | |
| # Overflow from multiplication. | |
| with self.assertRaises(OverflowError): | |
| math.fma(a, b, 0.0) | |
| self.assertEqual(math.fma(a, b/2.0, 0.0), c) | |
| # Overflow from the addition. | |
| with self.assertRaises(OverflowError): | |
| math.fma(a, b/2.0, c) | |
| # No overflow, even though a*b overflows a float. | |
| self.assertEqual(math.fma(a, b, -c), c) | |
| # Extreme case: a * b is exactly at the overflow boundary, so the | |
| # tiniest offset makes a difference between overflow and a finite | |
| # result. | |
| a = float.fromhex('0x1.ffffffc000000p+511') | |
| b = float.fromhex('0x1.0000002000000p+512') | |
| c = float.fromhex('0x0.0000000000001p-1022') | |
| with self.assertRaises(OverflowError): | |
| math.fma(a, b, 0.0) | |
| with self.assertRaises(OverflowError): | |
| math.fma(a, b, c) | |
| self.assertEqual(math.fma(a, b, -c), | |
| float.fromhex('0x1.fffffffffffffp+1023')) | |
| # Another extreme case: here a*b is about as large as possible subject | |
| # to math.fma(a, b, c) being finite. | |
| a = float.fromhex('0x1.ae565943785f9p+512') | |
| b = float.fromhex('0x1.3094665de9db8p+512') | |
| c = float.fromhex('0x1.fffffffffffffp+1023') | |
| self.assertEqual(math.fma(a, b, -c), c) | |
| @support.skip_on_newlib | |
| def test_fma_single_round(self): | |
| a = float.fromhex('0x1p-50') | |
| self.assertEqual(math.fma(a - 1.0, a + 1.0, 1.0), a*a) | |
| @support.skip_on_newlib | |
| def test_fma_random(self): | |
| # A collection of randomly generated inputs for which the naive FMA | |
| # (with two rounds) gives a different result from a singly-rounded FMA. | |
| # tuples (a, b, c, expected) | |
| test_values = [ | |
| ('0x1.694adde428b44p-1', '0x1.371b0d64caed7p-1', | |
| '0x1.f347e7b8deab8p-4', '0x1.19f10da56c8adp-1'), | |
| ('0x1.605401ccc6ad6p-2', '0x1.ce3a40bf56640p-2', | |
| '0x1.96e3bf7bf2e20p-2', '0x1.1af6d8aa83101p-1'), | |
| ('0x1.e5abd653a67d4p-2', '0x1.a2e400209b3e6p-1', | |
| '0x1.a90051422ce13p-1', '0x1.37d68cc8c0fbbp+0'), | |
| ('0x1.f94e8efd54700p-2', '0x1.123065c812cebp-1', | |
| '0x1.458f86fb6ccd0p-1', '0x1.ccdcee26a3ff3p-1'), | |
| ('0x1.bd926f1eedc96p-1', '0x1.eee9ca68c5740p-1', | |
| '0x1.960c703eb3298p-2', '0x1.3cdcfb4fdb007p+0'), | |
| ('0x1.27348350fbccdp-1', '0x1.3b073914a53f1p-1', | |
| '0x1.e300da5c2b4cbp-1', '0x1.4c51e9a3c4e29p+0'), | |
| ('0x1.2774f00b3497bp-1', '0x1.7038ec336bff0p-2', | |
| '0x1.2f6f2ccc3576bp-1', '0x1.99ad9f9c2688bp-1'), | |
| ('0x1.51d5a99300e5cp-1', '0x1.5cd74abd445a1p-1', | |
| '0x1.8880ab0bbe530p-1', '0x1.3756f96b91129p+0'), | |
| ('0x1.73cb965b821b8p-2', '0x1.218fd3d8d5371p-1', | |
| '0x1.d1ea966a1f758p-2', '0x1.5217b8fd90119p-1'), | |
| ('0x1.4aa98e890b046p-1', '0x1.954d85dff1041p-1', | |
| '0x1.122b59317ebdfp-1', '0x1.0bf644b340cc5p+0'), | |
| ('0x1.e28f29e44750fp-1', '0x1.4bcc4fdcd18fep-1', | |
| '0x1.fd47f81298259p-1', '0x1.9b000afbc9995p+0'), | |
| ('0x1.d2e850717fe78p-3', '0x1.1dd7531c303afp-1', | |
| '0x1.e0869746a2fc2p-2', '0x1.316df6eb26439p-1'), | |
| ('0x1.cf89c75ee6fbap-2', '0x1.b23decdc66825p-1', | |
| '0x1.3d1fe76ac6168p-1', '0x1.00d8ea4c12abbp+0'), | |
| ('0x1.3265ae6f05572p-2', '0x1.16d7ec285f7a2p-1', | |
| '0x1.0b8405b3827fbp-1', '0x1.5ef33c118a001p-1'), | |
| ('0x1.c4d1bf55ec1a5p-1', '0x1.bc59618459e12p-2', | |
| '0x1.ce5b73dc1773dp-1', '0x1.496cf6164f99bp+0'), | |
| ('0x1.d350026ac3946p-1', '0x1.9a234e149a68cp-2', | |
| '0x1.f5467b1911fd6p-2', '0x1.b5cee3225caa5p-1'), | |
| ] | |
| for a_hex, b_hex, c_hex, expected_hex in test_values: | |
| with self.subTest(a_hex=a_hex, b_hex=b_hex, c_hex=c_hex, | |
| expected_hex=expected_hex): | |
| a = float.fromhex(a_hex) | |
| b = float.fromhex(b_hex) | |
| c = float.fromhex(c_hex) | |
| expected = float.fromhex(expected_hex) | |
| self.assertEqual(math.fma(a, b, c), expected) | |
| self.assertEqual(math.fma(b, a, c), expected) | |
| # Custom assertions. | |
| def assertIsNaN(self, value): | |
| self.assertTrue( | |
| math.isnan(value), | |
| msg="Expected a NaN, got {!r}".format(value) | |
| ) | |
| def assertIsPositiveZero(self, value): | |
| self.assertTrue( | |
| value == 0 and math.copysign(1, value) > 0, | |
| msg="Expected a positive zero, got {!r}".format(value) | |
| ) | |
| def assertIsNegativeZero(self, value): | |
| self.assertTrue( | |
| value == 0 and math.copysign(1, value) < 0, | |
| msg="Expected a negative zero, got {!r}".format(value) | |
| ) | |
| def load_tests(loader, tests, pattern): | |
| from doctest import DocFileSuite | |
| tests.addTest(DocFileSuite(os.path.join("mathdata", "ieee754.txt"))) | |
| return tests | |
| if __name__ == '__main__': | |
| unittest.main() |