[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