Mercurial > cpython-lru_cache-weakref
view Lib/test/test_math.py @ 87690:66c1c9f32567 default tip
Add use_weakref option to functools.lru_cache
The use_weakref option makes lru_cache create weak references to the function
arguments when creating the cache key, which prevents them from staying in the
memory forever even after all other references to them are gone.
This is a proof of concept for http://bugs.python.org/issue19859
| author | Radomir Dopieralski <devel@sheep.art.pl> |
|---|---|
| date | Tue, 03 Dec 2013 16:02:48 +0100 |
| parents | 1feeeb8992f8 |
| children |
line wrap: on
line source
# Python test set -- math module # XXXX Should not do tests around zero only from test.support import run_unittest, verbose, requires_IEEE_754 from test import support import unittest import math import os import platform import sys import struct import sysconfig eps = 1E-05 NAN = float('nan') INF = float('inf') NINF = float('-inf') # detect evidence of double-rounding: fsum is not always correctly # rounded on machines that suffer from double rounding. x, y = 1e16, 2.9999 # use temporary values to defeat peephole optimizer HAVE_DOUBLE_ROUNDING = (x + y == 1e16 + 4) # 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, 'math_testcases.txt') test_file = os.path.join(test_dir, '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 C doubles are represented in IEEE 754 binary64 format. """ n = struct.unpack('<q', struct.pack('<d', x))[0] if n < 0: n = ~(n+2**63) return n def ulps_check(expected, got, ulps=20): """Given non-NaN floats `expected` and `got`, check that they're equal to within the given number of ulps. Returns None on success and an error message on failure.""" ulps_error = to_ulps(got) - to_ulps(expected) if abs(ulps_error) <= ulps: return None return "error = {} ulps; permitted error = {} ulps".format(ulps_error, ulps) # Here's a pure Python version of the math.factorial algorithm, for # documentation and comparison purposes. # # Formula: # # factorial(n) = factorial_odd_part(n) << (n - count_set_bits(n)) # # where # # factorial_odd_part(n) = product_{i >= 0} product_{0 < j <= n >> i; j odd} j # # The outer product above is an infinite product, but once i >= n.bit_length, # (n >> i) < 1 and the corresponding term of the product is empty. So only the # finitely many terms for 0 <= i < n.bit_length() contribute anything. # # We iterate downwards from i == n.bit_length() - 1 to i == 0. The inner # product in the formula above starts at 1 for i == n.bit_length(); for each i # < n.bit_length() we get the inner product for i from that for i + 1 by # multiplying by all j in {n >> i+1 < j <= n >> i; j odd}. In Python terms, # this set is range((n >> i+1) + 1 | 1, (n >> i) + 1 | 1, 2). def count_set_bits(n): """Number of '1' bits in binary expansion of a nonnnegative integer.""" return 1 + count_set_bits(n & n - 1) if n else 0 def partial_product(start, stop): """Product of integers in range(start, stop, 2), computed recursively. start and stop should both be odd, with start <= stop. """ numfactors = (stop - start) >> 1 if not numfactors: return 1 elif numfactors == 1: return start else: mid = (start + numfactors) | 1 return partial_product(start, mid) * partial_product(mid, stop) def py_factorial(n): """Factorial of nonnegative integer n, via "Binary Split Factorial Formula" described at http://www.luschny.de/math/factorial/binarysplitfact.html """ inner = outer = 1 for i in reversed(range(n.bit_length())): inner *= partial_product((n >> i + 1) + 1 | 1, (n >> i) + 1 | 1) outer *= inner return outer << (n - count_set_bits(n)) def acc_check(expected, got, rel_err=2e-15, abs_err = 5e-323): """Determine whether non-NaN floats a and b are equal to within a (small) rounding error. The default values for rel_err and abs_err are chosen to be suitable for platforms where a float is represented by an IEEE 754 double. They allow an error of between 9 and 19 ulps.""" # need to special case infinities, since inf - inf gives nan if math.isinf(expected) and got == expected: return None error = got - expected permitted_error = max(abs_err, rel_err * abs(expected)) if abs(error) < permitted_error: return None return "error = {}; permitted error = {}".format(error, permitted_error) 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) 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, arg = lhs.split() rhs_pieces = rhs.split() exp = rhs_pieces[0] flags = rhs_pieces[1:] yield (id, fn, float(arg), 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) 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 ) class MathTests(unittest.TestCase): def ftest(self, name, value, expected): if abs(value-expected) > eps: # Use %r instead of %f so the error message # displays full precision. Otherwise discrepancies # in the last few bits will lead to very confusing # error messages self.fail('%s returned %r, expected %r' % (name, value, expected)) def testConstants(self): self.ftest('pi', math.pi, 3.1415926) self.ftest('e', math.e, 2.7182818) 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.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.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))) 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) # 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 testCeil(self): self.assertRaises(TypeError, math.ceil) self.assertEqual(int, type(math.ceil(0.5))) self.ftest('ceil(0.5)', math.ceil(0.5), 1) self.ftest('ceil(1.0)', math.ceil(1.0), 1) self.ftest('ceil(1.5)', math.ceil(1.5), 2) self.ftest('ceil(-0.5)', math.ceil(-0.5), 0) self.ftest('ceil(-1.0)', math.ceil(-1.0), -1) self.ftest('ceil(-1.5)', math.ceil(-1.5), -1) #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 TestNoCeil: pass self.ftest('ceil(TestCeil())', math.ceil(TestCeil()), 42) self.assertRaises(TypeError, math.ceil, TestNoCeil()) t = TestNoCeil() t.__ceil__ = lambda *args: args self.assertRaises(TypeError, math.ceil, t) self.assertRaises(TypeError, math.ceil, t, 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 testCos(self): self.assertRaises(TypeError, math.cos) self.ftest('cos(-pi/2)', math.cos(-math.pi/2), 0) self.ftest('cos(0)', math.cos(0), 1) self.ftest('cos(pi/2)', math.cos(math.pi/2), 0) 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))) 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) 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))) 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 testFactorial(self): self.assertEqual(math.factorial(0), 1) self.assertEqual(math.factorial(0.0), 1) total = 1 for i in range(1, 1000): total *= i self.assertEqual(math.factorial(i), total) self.assertEqual(math.factorial(float(i)), total) self.assertEqual(math.factorial(i), py_factorial(i)) self.assertRaises(ValueError, math.factorial, -1) self.assertRaises(ValueError, math.factorial, -1.0) self.assertRaises(ValueError, math.factorial, math.pi) self.assertRaises(OverflowError, math.factorial, sys.maxsize+1) self.assertRaises(OverflowError, math.factorial, 10e100) def testFloor(self): self.assertRaises(TypeError, math.floor) self.assertEqual(int, type(math.floor(0.5))) self.ftest('floor(0.5)', math.floor(0.5), 0) self.ftest('floor(1.0)', math.floor(1.0), 1) self.ftest('floor(1.5)', math.floor(1.5), 1) self.ftest('floor(-0.5)', math.floor(-0.5), -1) self.ftest('floor(-1.0)', math.floor(-1.0), -1) self.ftest('floor(-1.5)', math.floor(-1.5), -2) # pow() relies on floor() to check for integers # This fails on some platforms - so check it here self.ftest('floor(1.23e167)', math.floor(1.23e167), 1.23e167) self.ftest('floor(-1.23e167)', math.floor(-1.23e167), -1.23e167) #self.assertEqual(math.ceil(INF), INF) #self.assertEqual(math.ceil(NINF), NINF) #self.assertTrue(math.isnan(math.floor(NAN))) class TestFloor: def __floor__(self): return 42 class TestNoFloor: pass self.ftest('floor(TestFloor())', math.floor(TestFloor()), 42) self.assertRaises(TypeError, math.floor, TestNoFloor()) t = TestNoFloor() t.__floor__ = lambda *args: args self.assertRaises(TypeError, math.floor, t) self.assertRaises(TypeError, math.floor, t, 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) 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 @unittest.skipIf(HAVE_DOUBLE_ROUNDING, "fsum is not exact on machines with 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 http://code.activestate.com/recipes/393090/ """ 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), ([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')), ([1.7**(i+1)-1.7**i for i in range(1000)] + [-1.7**1000], -1.0), ([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')), ] 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)) def testHypot(self): self.assertRaises(TypeError, math.hypot) self.ftest('hypot(0,0)', math.hypot(0,0), 0) self.ftest('hypot(3,4)', math.hypot(3,4), 5) self.assertEqual(math.hypot(NAN, INF), INF) self.assertEqual(math.hypot(INF, NAN), INF) self.assertEqual(math.hypot(NAN, NINF), INF) self.assertEqual(math.hypot(NINF, NAN), INF) self.assertTrue(math.isnan(math.hypot(1.0, NAN))) self.assertTrue(math.isnan(math.hypot(NAN, -2.0))) def testLdexp(self): self.assertRaises(TypeError, math.ldexp) 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))) def testLog(self): self.assertRaises(TypeError, math.log) 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.assertRaises(ValueError, math.log, -1.5) self.assertRaises(ValueError, math.log, -10**1000) self.assertRaises(ValueError, math.log, NINF) self.assertEqual(math.log(INF), INF) self.assertTrue(math.isnan(math.log(NAN))) def testLog1p(self): self.assertRaises(TypeError, math.log1p) n= 2**90 self.assertAlmostEqual(math.log1p(n), math.log1p(float(n))) @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) # 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.assertRaises(ValueError, math.log2, -1.5) self.assertRaises(ValueError, math.log2, NINF) self.assertTrue(math.isnan(math.log2(NAN))) @requires_IEEE_754 # log2() is not accurate enough on Mac OS X Tiger (10.4) @support.requires_mac_ver(10, 5) 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) 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.assertRaises(ValueError, math.log10, -1.5) self.assertRaises(ValueError, math.log10, -10**1000) self.assertRaises(ValueError, math.log10, NINF) self.assertEqual(math.log(INF), INF) self.assertTrue(math.isnan(math.log10(NAN))) 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) # 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.assertRaises(ValueError, math.pow, 0., NINF) 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.assertRaises(ValueError, math.pow, -0., NINF) 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) 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(1)', math.sqrt(1), 1) self.ftest('sqrt(4)', math.sqrt(4), 2) self.assertEqual(math.sqrt(INF), INF) 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: 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) 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 @unittest.skipIf(sysconfig.get_config_var('TANH_PRESERVES_ZERO_SIGN') == 0, "system tanh() function doesn't copy the sign") 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(object): def __trunc__(self): return 23 class TestNoTrunc(object): pass self.assertEqual(math.trunc(TestTrunc()), 23) self.assertRaises(TypeError, math.trunc) self.assertRaises(TypeError, math.trunc, 1, 2) self.assertRaises(TypeError, math.trunc, TestNoTrunc()) 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 testIsnan(self): 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.)) # 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): for id, fn, ar, ai, er, ei, flags in parse_testfile(test_file): # Skip if either the input or result is complex, or if # flags is nonempty if ai != 0. or ei != 0. or flags: continue if fn in ['rect', 'polar']: # no real versions of rect, polar continue func = getattr(math, fn) try: result = func(ar) except ValueError as exc: message = (("Unexpected ValueError: %s\n " + "in test %s:%s(%r)\n") % (exc.args[0], id, fn, ar)) self.fail(message) except OverflowError: message = ("Unexpected OverflowError in " + "test %s:%s(%r)\n" % (id, fn, ar)) self.fail(message) self.ftest("%s:%s(%r)" % (id, fn, ar), result, er) @requires_IEEE_754 def test_mtestfile(self): fail_fmt = "{}:{}({!r}): expected {!r}, got {!r}" failures = [] for id, fn, arg, expected, flags in parse_mtestfile(math_testcases): 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(arg) except ValueError: got = 'ValueError' except OverflowError: got = 'OverflowError' accuracy_failure = None if isinstance(got, float) and isinstance(expected, float): if math.isnan(expected) and math.isnan(got): continue if not math.isnan(expected) and not math.isnan(got): if 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. accuracy_failure = acc_check(expected, got, rel_err = 5e-15, abs_err = 5e-15) elif fn == 'erfc': # erfc has less-than-ideal accuracy for large # arguments (x ~ 25 or so), mainly due to the # error involved in computing exp(-x*x). # # XXX Would be better to weaken this test only # for large x, instead of for all x. accuracy_failure = ulps_check(expected, got, 2000) else: accuracy_failure = ulps_check(expected, got, 20) if accuracy_failure is None: continue if isinstance(got, str) and isinstance(expected, str): if got == expected: continue fail_msg = fail_fmt.format(id, fn, arg, expected, got) if accuracy_failure is not None: fail_msg += ' ({})'.format(accuracy_failure) failures.append(fail_msg) if failures: self.fail('Failures in test_mtestfile:\n ' + '\n '.join(failures)) def test_main(): from doctest import DocFileSuite suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(MathTests)) suite.addTest(DocFileSuite("ieee754.txt")) run_unittest(suite) if __name__ == '__main__': test_main()
