Author: Tim Felgentreff <timfelgentr...@gmail.com> Branch: Changeset: r61493:67e7e4a54f22 Date: 2013-02-20 12:56 +0100 http://bitbucket.org/pypy/pypy/changeset/67e7e4a54f22/
Log: (timfel, cfbolz) move float_as_integer_ratio into rlib and add test, because Ruby needs it, too diff --git a/pypy/objspace/std/floatobject.py b/pypy/objspace/std/floatobject.py --- a/pypy/objspace/std/floatobject.py +++ b/pypy/objspace/std/floatobject.py @@ -10,7 +10,7 @@ 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 @@ -553,27 +553,18 @@ 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): diff --git a/rpython/rlib/rfloat.py b/rpython/rlib/rfloat.py --- a/rpython/rlib/rfloat.py +++ b/rpython/rlib/rfloat.py @@ -419,3 +419,25 @@ 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 diff --git a/rpython/rlib/test/test_rfloat.py b/rpython/rlib/test/test_rfloat.py new file mode 100644 --- /dev/null +++ b/rpython/rlib/test/test_rfloat.py @@ -0,0 +1,23 @@ +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')) _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit