[pypy-commit] pypy default: add a half even rounding mode to round_double for py3k
pjenvey
noreply at buildbot.pypy.org
Sat Dec 15 01:05:10 CET 2012
Author: Philip Jenvey <pjenvey at 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):
More information about the pypy-commit
mailing list