[pypy-svn] r78710 - in pypy/branch/fast-forward/pypy: module/__builtin__ objspace/std/test
afa at codespeak.net
afa at codespeak.net
Thu Nov 4 23:25:27 CET 2010
Author: afa
Date: Thu Nov 4 23:25:25 2010
New Revision: 78710
Modified:
pypy/branch/fast-forward/pypy/module/__builtin__/operation.py
pypy/branch/fast-forward/pypy/objspace/std/test/test_floatobject.py
Log:
Test and fix extreme cases for round()
Modified: pypy/branch/fast-forward/pypy/module/__builtin__/operation.py
==============================================================================
--- pypy/branch/fast-forward/pypy/module/__builtin__/operation.py (original)
+++ pypy/branch/fast-forward/pypy/module/__builtin__/operation.py Thu Nov 4 23:25:25 2010
@@ -8,6 +8,9 @@
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.typedef import TypeDef
from pypy.rlib.runicode import UNICHR
+from pypy.rlib.rarithmetic import isnan, isinf
+from pypy.rlib import rfloat
+import math
import __builtin__
NoneNotWrapped = gateway.NoneNotWrapped
@@ -140,38 +143,65 @@
# ____________________________________________________________
-from math import floor as _floor
-from math import ceil as _ceil
+# Here 0.30103 is an upper bound for log10(2)
+NDIGITS_MAX = int((rfloat.DBL_MANT_DIG - rfloat.DBL_MIN_EXP) * 0.30103)
+NDIGITS_MIN = -int((rfloat.DBL_MAX_EXP + 1) * 0.30103)
-def round(space, number, ndigits=0):
+def round(space, number, w_ndigits=0):
"""round(number[, ndigits]) -> floating point number
Round a number to a given precision in decimal digits (default 0 digits).
This always returns a floating point number. Precision may be negative."""
- # Algortithm copied directly from CPython
- f = 1.0
- if ndigits < 0:
- i = -ndigits
- else:
- i = ndigits
- while i > 0:
- f = f*10.0
- i -= 1
- if ndigits < 0:
- number /= f
+ # Algorithm copied directly from CPython
+
+ # interpret 2nd argument as a Py_ssize_t; clip on overflow
+ ndigits = space.getindex_w(w_ndigits, None)
+
+ # nans, infinities and zeros round to themselves
+ if number == 0 or isinf(number) or isnan(number):
+ return space.wrap(number)
+
+ # Deal with extreme values for ndigits. For ndigits > NDIGITS_MAX, x
+ # always rounds to itself. For ndigits < NDIGITS_MIN, x always
+ # rounds to +-0.0.
+ if ndigits > NDIGITS_MAX:
+ return space.wrap(number)
+ elif ndigits < NDIGITS_MIN:
+ # return 0.0, but with sign of x
+ return space.wrap(0.0 * number)
+
+ if ndigits >= 0:
+ if ndigits > 22:
+ # pow1 and pow2 are each safe from overflow, but
+ # pow1*pow2 ~= pow(10.0, ndigits) might overflow
+ pow1 = math.pow(10.0, ndigits - 22)
+ pow2 = 1e22
+ else:
+ pow1 = math.pow(10.0, ndigits)
+ pow2 = 1.0
+
+ y = (number * pow1) * pow2
+ # if y overflows, then rounded value is exactly x
+ if isinf(y):
+ return space.wrap(number)
+
else:
- number *= f
- if number >= 0.0:
- number = _floor(number + 0.5)
+ pow1 = math.pow(10.0, -ndigits);
+ pow2 = 1.0 # unused; for translation
+ y = number / pow1
+
+ if y >= 0.0:
+ z = math.floor(y + 0.5)
else:
- number = _ceil(number - 0.5)
- if ndigits < 0:
- number *= f
+ z = math.ceil(y - 0.5)
+
+ if ndigits >= 0:
+ z = (z / pow2) / pow1
else:
- number /= f
- return space.wrap(number)
+ z *= pow1
+ return space.wrap(z)
#
-round.unwrap_spec = [ObjSpace, float, int]
+round.unwrap_spec = [ObjSpace, float, W_Root]
# ____________________________________________________________
Modified: pypy/branch/fast-forward/pypy/objspace/std/test/test_floatobject.py
==============================================================================
--- pypy/branch/fast-forward/pypy/objspace/std/test/test_floatobject.py (original)
+++ pypy/branch/fast-forward/pypy/objspace/std/test/test_floatobject.py Thu Nov 4 23:25:25 2010
@@ -147,6 +147,7 @@
assert float(X()) == 42.
def test_round(self):
+ import math
assert 1.0 == round(1.0)
assert 1.0 == round(1.1)
assert 2.0 == round(1.9)
@@ -158,6 +159,11 @@
assert 22.2 == round(22.222222, 1)
assert 20.0 == round(22.22222, -1)
assert 0.0 == round(22.22222, -2)
+ #
+ assert round(123.456, -308) == 0.0
+ assert round(123.456, -700) == 0.0
+ assert round(123.456, -2**100) == 0.0
+ assert math.copysign(1., round(-123.456, -700)) == -1.
def test_special_float_method(self):
class a(object):
More information about the Pypy-commit
mailing list