[pypy-svn] r75844 - in pypy/branch/fast-forward/pypy/module/math: . test
benjamin at codespeak.net
benjamin at codespeak.net
Mon Jul 5 18:54:13 CEST 2010
Author: benjamin
Date: Mon Jul 5 18:54:12 2010
New Revision: 75844
Modified:
pypy/branch/fast-forward/pypy/module/math/__init__.py
pypy/branch/fast-forward/pypy/module/math/interp_math.py
pypy/branch/fast-forward/pypy/module/math/test/test_math.py
Log:
add math.fsum: floating point summation with partials
Modified: pypy/branch/fast-forward/pypy/module/math/__init__.py
==============================================================================
--- pypy/branch/fast-forward/pypy/module/math/__init__.py (original)
+++ pypy/branch/fast-forward/pypy/module/math/__init__.py Mon Jul 5 18:54:12 2010
@@ -38,5 +38,6 @@
'isinf' : 'interp_math.isinf',
'isnan' : 'interp_math.isnan',
'trunc' : 'interp_math.trunc',
+ 'fsum' : 'interp_math.fsum',
}
Modified: pypy/branch/fast-forward/pypy/module/math/interp_math.py
==============================================================================
--- pypy/branch/fast-forward/pypy/module/math/interp_math.py (original)
+++ pypy/branch/fast-forward/pypy/module/math/interp_math.py Mon Jul 5 18:54:12 2010
@@ -299,3 +299,68 @@
"""
return math1(space, math.acos, x)
acos.unwrap_spec = [ObjSpace, float]
+
+def fsum(space, w_iterable):
+ """Sum an iterable of floats, trying to keep precision."""
+ w_iter = space.iter(w_iterable)
+ inf_sum = special_sum = 0.0
+ partials = []
+ while True:
+ try:
+ w_value = space.next(w_iter)
+ except OperationError, e:
+ if not e.match(space, space.w_StopIteration):
+ raise
+ break
+ v = space.float_w(w_value)
+ original = v
+ added = 0
+ for y in partials:
+ if abs(v) < abs(y):
+ v, y = y, v
+ hi = v + y
+ yr = hi - v
+ lo = y - yr
+ if lo != 0.0:
+ partials[added] = lo
+ added += 1
+ v = hi
+ del partials[added:]
+ if v != 0.0:
+ if rarithmetic.isinf(v) or rarithmetic.isnan(v):
+ if (not rarithmetic.isinf(original) and
+ not rarithmetic.isnan(original)):
+ raise OperationError(space.w_OverflowError,
+ space.wrap("intermediate overflow"))
+ if isinf(original):
+ inf_sum += original
+ special_sum += original
+ del partials[:]
+ else:
+ partials.append(v)
+ if special_sum != 0.0:
+ if rarithmetic.isnan(special_sum):
+ raise OperationError(space.w_ValueError, space.wrap("-inf + inf"))
+ return space.wrap(special_sum)
+ hi = 0.0
+ if partials:
+ hi = partials[-1]
+ j = 0
+ for j in range(len(partials) - 2, -1, -1):
+ v = hi
+ y = partials[j]
+ assert abs(y) < abs(v)
+ hi = v + y
+ yr = hi - v
+ lo = y - yr
+ if lo != 0.0:
+ break
+ if j > 0 and (lo < 0.0 and partials[j - 1] < 0.0 or
+ lo > 0.0 and partials[j - 1] > 0.0):
+ y = lo * 2.0
+ v = hi + y
+ yr = v - hi
+ if y == yr:
+ hi = v
+ return space.wrap(hi)
+fsum.unwrap_spec = [ObjSpace, W_Root]
Modified: pypy/branch/fast-forward/pypy/module/math/test/test_math.py
==============================================================================
--- pypy/branch/fast-forward/pypy/module/math/test/test_math.py (original)
+++ pypy/branch/fast-forward/pypy/module/math/test/test_math.py Mon Jul 5 18:54:12 2010
@@ -35,3 +35,41 @@
if not ok:
raise AssertionError("%s(%s): got %s" % (
fnname, ', '.join(map(str, args)), got))
+
+ def test_fsum(self):
+ # Python version of math.fsum, for comparison. Uses a
+ # different algorithm based on frexp, ldexp and integer
+ # arithmetic.
+ import math
+
+ 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:
+ py.test.fail("test %d failed: got OverflowError, expected %r "
+ "for math.fsum(%.100r)" % (i, expected, vals))
+ except ValueError:
+ py.test.fail("test %d failed: got ValueError, expected %r "
+ "for math.fsum(%.100r)" % (i, expected, vals))
+ assert actual == expected
More information about the Pypy-commit
mailing list