timfel avatar timfel committed 67e7e4a

(timfel, cfbolz) move float_as_integer_ratio into rlib and add test, because Ruby needs it, too

Comments (0)

Files changed (3)

pypy/objspace/std/floatobject.py

 from rpython.rlib.rarithmetic import ovfcheck_float_to_int, intmask, LONG_BIT
 from rpython.rlib.rfloat import (
     isinf, isnan, isfinite, INFINITY, NAN, copysign, formatd,
-    DTSF_ADD_DOT_0, DTSF_STR_PRECISION)
+    DTSF_ADD_DOT_0, DTSF_STR_PRECISION, float_as_rbigint_ratio)
 from rpython.rlib.rbigint import rbigint
 from rpython.rlib import rfloat
 from rpython.tool.sourcetools import func_with_new_name
 
 def float_as_integer_ratio__Float(space, w_float):
     value = w_float.floatval
-    if isinf(value):
+    try:
+        num, den = float_as_rbigint_ratio(value)
+    except OverflowError:
         w_msg = space.wrap("cannot pass infinity to as_integer_ratio()")
         raise OperationError(space.w_OverflowError, w_msg)
-    elif isnan(value):
+    except ValueError:
         w_msg = space.wrap("cannot pass nan to as_integer_ratio()")
         raise OperationError(space.w_ValueError, w_msg)
-    float_part, exp = math.frexp(value)
-    for i in range(300):
-        if float_part == math.floor(float_part):
-            break
-        float_part *= 2.0
-        exp -= 1
-    w_num = W_LongObject.fromfloat(space, float_part)
-    w_den = space.newlong(1)
-    w_exp = space.newlong(abs(exp))
-    w_exp = space.lshift(w_den, w_exp)
-    if exp > 0:
-        w_num = space.mul(w_num, w_exp)
-    else:
-        w_den = w_exp
-    # Try to return int.
+
+    w_num = space.newlong_from_rbigint(num)
+    w_den = space.newlong_from_rbigint(den)
+    # Try to return int
     return space.newtuple([space.int(w_num), space.int(w_den)])
 
 def float_is_integer__Float(space, w_float):

rpython/rlib/rfloat.py

 def isfinite(x):
     "NOT_RPYTHON"
     return not isinf(x) and not isnan(x)
+
+def float_as_rbigint_ratio(value):
+    from rpython.rlib.rbigint import rbigint
+
+    if isinf(value):
+        raise OverflowError("cannot pass infinity to as_integer_ratio()")
+    elif isnan(value):
+        raise ValueError("cannot pass nan to as_integer_ratio()")
+    float_part, exp_int = math.frexp(value)
+    for i in range(300):
+        if float_part == math.floor(float_part):
+            break
+        float_part *= 2.0
+        exp_int -= 1
+    num = rbigint.fromfloat(float_part)
+    den = rbigint.fromint(1)
+    exp = den.lshift(abs(exp_int))
+    if exp_int > 0:
+        num = num.mul(exp)
+    else:
+        den = exp
+    return num, den

rpython/rlib/test/test_rfloat.py

+import sys, py
+
+from rpython.rlib.rfloat import float_as_rbigint_ratio
+from rpython.rlib.rbigint import rbigint
+
+
+def test_float_as_rbigint_ratio():
+    for f, ratio in [
+        (0.875, (7, 8)),
+        (-0.875, (-7, 8)),
+        (0.0, (0, 1)),
+        (11.5, (23, 2)),
+        ]:
+        num, den = float_as_rbigint_ratio(f)
+        assert num.eq(rbigint.fromint(ratio[0]))
+        assert den.eq(rbigint.fromint(ratio[1]))
+
+    with py.test.raises(OverflowError):
+        float_as_rbigint_ratio(float('inf'))
+    with py.test.raises(OverflowError):
+        float_as_rbigint_ratio(float('-inf'))
+    with py.test.raises(ValueError):
+        float_as_rbigint_ratio(float('nan'))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.