diff options
author | Mark Dickinson <dickinsm@gmail.com> | 2008-05-23 02:36:48 (GMT) |
---|---|---|
committer | Mark Dickinson <dickinsm@gmail.com> | 2008-05-23 02:36:48 (GMT) |
commit | c11c3391b0fd3bb0c92526f0aa0c8232963416be (patch) | |
tree | a85fffac725c3301b1f09834926ddba8d1228dce /Lib/test/test_math.py | |
parent | 99dfe927590ef95b9ce42f8334e3126c5960ad6f (diff) | |
download | cpython-c11c3391b0fd3bb0c92526f0aa0c8232963416be.zip cpython-c11c3391b0fd3bb0c92526f0aa0c8232963416be.tar.gz cpython-c11c3391b0fd3bb0c92526f0aa0c8232963416be.tar.bz2 |
Add tests for math.sum (Issue #2819)
Diffstat (limited to 'Lib/test/test_math.py')
-rw-r--r-- | Lib/test/test_math.py | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index a5c6f62..e727d9d 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -630,6 +630,142 @@ class MathTests(unittest.TestCase): self.assertRaises(ValueError, math.sqrt, NINF) self.assert_(math.isnan(math.sqrt(NAN))) + def testSum(self): + # Python version of math.sum algorithm, for comparison + def msum(iterable): + """Full precision sum of values in iterable. Returns the value of + the sum, rounded to the nearest representable floating-point number + using the round-half-to-even rule. + + """ + # Stage 1: accumulate partials + partials = [] + for x in iterable: + i = 0 + for y in partials: + if abs(x) < abs(y): + x, y = y, x + hi = x + y + lo = y - (hi - x) + if lo: + partials[i] = lo + i += 1 + x = hi + partials[i:] = [x] if x else [] + + # Stage 2: sum partials + if not partials: + return 0.0 + + # sum from the top, stopping as soon as the sum is inexact. + total = partials.pop() + while partials: + x = partials.pop() + old_total, total = total, total + x + error = x - (total - old_total) + if error != 0.0: + # adjust for correct rounding if necessary + if partials and (partials[-1] > 0.0) == (error > 0.0) and \ + total + 2*error - total == 2*error: + total += 2*error + break + return total + + from sys import float_info + maxfloat = float_info.max + twopow = 2.**(float_info.max_exp - 1) + + test_values = [ + ([], 0.0), + ([0.0], 0.0), + ([1e100, 1.0, -1e100, 1e-100, 1e50, -1.0, -1e50], 1e-100), + ([1e308, 1e308, -1e308], OverflowError), + ([-1e308, 1e308, 1e308], 1e308), + ([1e308, -1e308, 1e308], 1e308), + ([2.0**1023, 2.0**1023, -2.0**1000], OverflowError), + ([twopow, twopow, twopow, twopow, -twopow, -twopow, -twopow], + OverflowError), + ([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), + ([2.0**1023-2.0**970, -1.0, 2.0**1023], OverflowError), + ([maxfloat, maxfloat*2.**-54], maxfloat), + ([maxfloat, maxfloat*2.**-53], OverflowError), + ([1./n for n in range(1, 1001)], 7.4854708605503451), + ([(-1.)**n/n for n in range(1, 1001)], -0.69264743055982025), + ([1.7**(i+1)-1.7**i for i in range(1000)] + [-1.7**1000], -1.0), + ([INF, -INF, NAN], ValueError), + ([NAN, INF, -INF], ValueError), + ([INF, NAN, INF], ValueError), + + ([INF, INF], OverflowError), + ([INF, -INF], ValueError), + ([-INF, 1e308, 1e308, -INF], OverflowError), + ([2.0**1023-2.0**970, 0.0, 2.0**1023], OverflowError), + ([2.0**1023-2.0**970, 1.0, 2.0**1023], OverflowError), + ([2.0**1023, 2.0**1023], OverflowError), + ([2.0**1023, 2.0**1023, -1.0], OverflowError), + ([twopow, twopow, twopow, twopow, -twopow, -twopow], + OverflowError), + ([twopow, twopow, twopow, twopow, -twopow, twopow], OverflowError), + ([-twopow, -twopow, -twopow, -twopow], OverflowError), + + ([2.**1023, 2.**1023, -2.**971], OverflowError), + ([2.**1023, 2.**1023, -2.**970], OverflowError), + ([-2.**970, 2.**1023, 2.**1023, -2.**-1074], OverflowError), + ([ 2.**1023, 2.**1023, -2.**970, 2.**-1074], OverflowError), + ([-2.**1023, 2.**971, -2.**1023], -maxfloat), + ([-2.**1023, -2.**1023, 2.**970], OverflowError), + ([-2.**1023, -2.**1023, 2.**970, 2.**-1074], OverflowError), + ([-2.**-1074, -2.**1023, -2.**1023, 2.**970], OverflowError), + ([2.**930, -2.**980, 2.**1023, 2.**1023, twopow, -twopow], + OverflowError), + ([2.**1023, 2.**1023, -1e307], OverflowError), + ([1e16, 1., 1e-16], 10000000000000002.0), + ([1e16-2., 1.-2.**53, -(1e16-2.), -(1.-2.**53)], 0.0), + ] + + for i, (vals, s) in enumerate(test_values): + if isinstance(s, type) and issubclass(s, Exception): + try: + m = math.sum(vals) + except s: + pass + else: + self.fail("test %d failed: got %r, expected %r " + "for math.sum(%.100r)" % + (i, m, s.__name__, vals)) + else: + try: + self.assertEqual(math.sum(vals), s) + except OverflowError: + self.fail("test %d failed: got OverflowError, expected %r " + "for math.sum(%.100r)" % (i, s, vals)) + except ValueError: + self.fail("test %d failed: got ValueError, expected %r " + "for math.sum(%.100r)" % (i, s, vals)) + + # compare with output of msum above, but only when + # result isn't an IEEE special or an exception + if not math.isinf(s) and not math.isnan(s): + self.assertEqual(msum(vals), s) + + from random import random, gauss, shuffle + for j in xrange(1000): + vals = [7, 1e100, -7, -1e100, -9e-20, 8e-20] * 10 + s = 0 + for i in xrange(200): + v = gauss(0, random()) ** 7 - s + s += v + vals.append(v) + shuffle(vals) + + s = msum(vals) + self.assertEqual(msum(vals), math.sum(vals)) + + def testTan(self): self.assertRaises(TypeError, math.tan) self.ftest('tan(0)', math.tan(0), 0) |