Author: Philip Jenvey <pjen...@underboss.org> Branch: Changeset: r59425:1fce20d13b3c Date: 2012-12-14 16:03 -0800 http://bitbucket.org/pypy/pypy/changeset/1fce20d13b3c/
Log: add a half even rounding mode to round_double for py3k diff --git a/pypy/rlib/rfloat.py b/pypy/rlib/rfloat.py --- a/pypy/rlib/rfloat.py +++ b/pypy/rlib/rfloat.py @@ -170,13 +170,17 @@ result = formatd(value, tp, precision, flags) return result, special -def round_double(value, ndigits): +def round_double(value, ndigits, half_even=False): + """Round a float half away from zero. + + Specify half_even=True to round half even instead. + """ if USE_SHORT_FLOAT_REPR: - return round_double_short_repr(value, ndigits) + return round_double_short_repr(value, ndigits, half_even) else: - return round_double_fallback_repr(value, ndigits) + return round_double_fallback_repr(value, ndigits, half_even) -def round_double_short_repr(value, ndigits): +def round_double_short_repr(value, ndigits, half_even): # The basic idea is very simple: convert and round the double to # a decimal string using _Py_dg_dtoa, then convert that decimal # string back to a double with _Py_dg_strtod. There's one minor @@ -209,7 +213,7 @@ # determine whether this is a halfway case. halfway_case = 0 - if expo == -ndigits - 1: + if not half_even and expo == -ndigits - 1: if ndigits >= 0: halfway_case = 1 elif ndigits >= -22: @@ -224,7 +228,7 @@ # round to a decimal string; use an extra place for halfway case strvalue = formatd(value, 'f', ndigits + halfway_case) - if halfway_case: + if not half_even and halfway_case: buf = [c for c in strvalue] if ndigits >= 0: endpos = len(buf) - 1 @@ -263,7 +267,7 @@ # fallback version, to be used when correctly rounded # binary<->decimal conversions aren't available -def round_double_fallback_repr(value, ndigits): +def round_double_fallback_repr(value, ndigits, half_even): if ndigits >= 0: if ndigits > 22: # pow1 and pow2 are each safe from overflow, but @@ -284,12 +288,17 @@ pow2 = 1.0 # unused; for translation y = value / pow1 - if y >= 0.0: - z = math.floor(y + 0.5) + if half_even: + z = round_away(y) + if math.fabs(y - z) == 0.5: + z = 2.0 * round_away(y / 2.0) else: - z = math.ceil(y - 0.5) - if math.fabs(y-z) == 1.0: # obscure case, see the test - z = y + if y >= 0.0: + z = math.floor(y + 0.5) + else: + z = math.ceil(y - 0.5) + if math.fabs(y - z) == 1.0: # obscure case, see the test + z = y if ndigits >= 0: z = (z / pow2) / pow1 diff --git a/pypy/rpython/test/test_rfloat.py b/pypy/rpython/test/test_rfloat.py --- a/pypy/rpython/test/test_rfloat.py +++ b/pypy/rpython/test/test_rfloat.py @@ -374,6 +374,15 @@ almost_equal(round_double(0.5e22, -22), 1e22) almost_equal(round_double(1.5e22, -22), 2e22) + def test_round_half_even(self): + from pypy.rlib import rfloat + for func in (rfloat.round_double_short_repr, + rfloat.round_double_fallback_repr): + # 2.x behavior + assert func(2.5, 0, False) == 3.0 + # 3.x behavior + assert func(2.5, 0, True) == 2.0 + class TestLLtype(BaseTestRfloat, LLRtypeMixin): _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit