[pypy-svn] pypy shorter-float-repr: Translate from CPython into RPython: format_float_short()
amauryfa
commits-noreply at bitbucket.org
Fri Jan 21 01:12:52 CET 2011
Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: shorter-float-repr
Changeset: r41106:ab8bb12b6dc2
Date: 2011-01-21 01:12 +0100
http://bitbucket.org/pypy/pypy/changeset/ab8bb12b6dc2/
Log: Translate from CPython into RPython: format_float_short()
diff --git a/pypy/rpython/module/ll_dtoa.py b/pypy/rpython/module/ll_dtoa.py
--- a/pypy/rpython/module/ll_dtoa.py
+++ b/pypy/rpython/module/ll_dtoa.py
@@ -47,8 +47,156 @@
raise ValueError("invalid input at position %d" % (offset,))
return result
-def dtoa(value, mode=0, precision=0, flags=0):
+def format_nonfinite(digits, sign, flags):
+ "Format dtoa's output for nonfinite numbers"
+ if digits[0] == 'i' or digits[0] == 'I':
+ if sign == 1:
+ return '-inf'
+ elif flags & rarithmetic.DTSF_SIGN:
+ return '+inf'
+ else:
+ return 'inf'
+ elif digits[0] == 'n' or digits[0] == 'N':
+ return 'nan'
+ else:
+ # shouldn't get here
+ raise ValueError
+
+def format_number(digits, buflen, sign, decpt, code, precision, flags):
+ # We got digits back, format them. We may need to pad 'digits'
+ # either on the left or right (or both) with extra zeros, so in
+ # general the resulting string has the form
+ #
+ # [<sign>]<zeros><digits><zeros>[<exponent>]
+ #
+ # where either of the <zeros> pieces could be empty, and there's a
+ # decimal point that could appear either in <digits> or in the
+ # leading or trailing <zeros>.
+ #
+ # Imagine an infinite 'virtual' string vdigits, consisting of the
+ # string 'digits' (starting at index 0) padded on both the left
+ # and right with infinite strings of zeros. We want to output a
+ # slice
+ #
+ # vdigits[vdigits_start : vdigits_end]
+ #
+ # of this virtual string. Thus if vdigits_start < 0 then we'll
+ # end up producing some leading zeros; if vdigits_end > digits_len
+ # there will be trailing zeros in the output. The next section of
+ # code determines whether to use an exponent or not, figures out
+ # the position 'decpt' of the decimal point, and computes
+ # 'vdigits_start' and 'vdigits_end'.
builder = StringBuilder(20)
+
+ use_exp = False
+ vdigits_end = buflen
+ if code == 'e':
+ use_exp = True
+ vdigits_end = precision
+ elif code == 'f':
+ vdigits_end = decpt + precision
+ elif code == 'g':
+ if decpt <= -4:
+ use_exp = True
+ elif decpt > precision:
+ use_exp = True
+ elif flags & rarithmetic.DTSF_ADD_DOT_0 and decpt == precision:
+ use_exp = True
+ if flags & rarithmetic.DTSF_ALT:
+ vdigits_end = precision
+ elif code == 'r':
+ # convert to exponential format at 1e16. We used to convert
+ # at 1e17, but that gives odd-looking results for some values
+ # when a 16-digit 'shortest' repr is padded with bogus zeros.
+ # For example, repr(2e16+8) would give 20000000000000010.0;
+ # the true value is 20000000000000008.0.
+ if decpt <= -4 or decpt > 16:
+ use_exp = True
+ else:
+ raise ValueError
+
+ # if using an exponent, reset decimal point position to 1 and
+ # adjust exponent accordingly.
+ if use_exp:
+ exp = decpt - 1
+ decpt = 1
+
+ # ensure vdigits_start < decpt <= vdigits_end, or vdigits_start <
+ # decpt < vdigits_end if add_dot_0_if_integer and no exponent
+ if decpt <= 0:
+ vdigits_start = decpt-1
+ else:
+ vdigits_start = 0
+ if vdigits_end <= decpt:
+ if not use_exp and flags & rarithmetic.DTSF_ADD_DOT_0:
+ vdigits_end = decpt + 1
+ else:
+ vdigits_end = decpt
+
+ # double check inequalities
+ assert vdigits_start <= 0
+ assert 0 <= buflen <= vdigits_end
+ # decimal point should be in (vdigits_start, vdigits_end]
+ assert vdigits_start < decpt <= vdigits_end
+
+ if sign == 1:
+ builder.append('-')
+ elif flags & rarithmetic.DTSF_SIGN:
+ builder.append('+')
+
+ # note that exactly one of the three 'if' conditions is true, so
+ # we include exactly one decimal point
+ # 1. Zero padding on left of digit string
+ if decpt <= 0:
+ builder.append_multiple_char('0', decpt - vdigits_start)
+ builder.append('.')
+ builder.append_multiple_char('0', 0 - decpt)
+ else:
+ builder.append_multiple_char('0', 0 - vdigits_start)
+
+ # 2. Digits, with included decimal point
+ if 0 < decpt <= buflen:
+ builder.append(rffi.charpsize2str(digits, decpt - 0))
+ builder.append('.')
+ ptr = rffi.ptradd(digits, decpt)
+ builder.append(rffi.charpsize2str(ptr, buflen - decpt))
+ else:
+ builder.append(rffi.charpsize2str(digits, buflen))
+
+ # 3. And zeros on the right
+ if buflen < decpt:
+ builder.append_multiple_char('0', decpt - buflen)
+ builder.append('.')
+ builder.append_multiple_char('0', vdigits_end - decpt)
+ else:
+ builder.append_multiple_char('0', vdigits_end - buflen)
+
+ s = builder.build()
+
+ # Delete a trailing decimal pt unless using alternative formatting.
+ if not flags & rarithmetic.DTSF_ALT:
+ last = len(s) - 1
+ if last >= 0 and s[last] == '.':
+ s = s[:last]
+
+ # Now that we've done zero padding, add an exponent if needed.
+ if use_exp:
+ if exp >= 0:
+ exp_str = str(exp)
+ if len(exp_str) < 2:
+ s += 'e+0' + exp_str
+ else:
+ s += 'e+' + exp_str
+ else:
+ exp_str = str(-exp)
+ if len(exp_str) < 2:
+ s += 'e-0' + exp_str
+ else:
+ s += 'e-' + exp_str
+
+ return s
+
+def dtoa(value, code='r', mode=0, precision=0, flags=0):
with lltype.scoped_alloc(rffi.INTP.TO, 1) as decpt_ptr:
with lltype.scoped_alloc(rffi.INTP.TO, 1) as sign_ptr:
with lltype.scoped_alloc(rffi.CCHARPP.TO, 1) as end_ptr:
@@ -61,42 +209,15 @@
# Handle nan and inf
if buflen and not digits[0].isdigit():
- if digits[0] == 'i' or digits[0] == 'I':
- if sign == 1:
- builder.append('-')
- elif flags & rarithmetic.DTSF_SIGN:
- builder.append('+')
- builder.append('inf')
- elif digits[0] == 'n' or digits[0] == 'N':
- builder.append('nan')
- else:
- # shouldn't get here
- raise ValueError
- return builder.build()
+ return format_nonfinite(digits, sign, flags)
- if sign == 1:
- builder.append('-')
- elif flags & rarithmetic.DTSF_SIGN:
- builder.append('+')
+ decpt = rffi.cast(lltype.Signed, decpt_ptr[0])
- intpart = rffi.cast(lltype.Signed, decpt_ptr[0])
- if intpart <= buflen:
- builder.append(rffi.charpsize2str(digits, intpart))
- else:
- builder.append(rffi.charpsize2str(digits, buflen))
- while buflen < intpart:
- builder.append('0')
- intpart -= 1
- fracpart = buflen - intpart
- if fracpart > 0:
- builder.append('.')
- ptr = rffi.ptradd(digits, intpart)
- builder.append(rffi.charpsize2str(ptr, fracpart))
- elif flags & rarithmetic.DTSF_ADD_DOT_0:
- builder.append('.0')
+ return format_number(digits, buflen, sign, decpt,
+ code, precision, flags)
+
finally:
dg_freedtoa(digits)
- return builder.build()
def llimpl_strtod(value, code, precision, flags):
if code in 'EFG':
@@ -119,4 +240,4 @@
else:
raise ValueError('Invalid mode')
- return dtoa(value, mode=mode, precision=precision, flags=flags)
+ return dtoa(value, code, mode=mode, precision=precision, flags=flags)
diff --git a/pypy/rpython/module/test/test_ll_dtoa.py b/pypy/rpython/module/test/test_ll_dtoa.py
--- a/pypy/rpython/module/test/test_ll_dtoa.py
+++ b/pypy/rpython/module/test/test_ll_dtoa.py
@@ -13,8 +13,11 @@
assert dtoa(1.1, flags=rarithmetic.DTSF_SIGN) == "+1.1"
assert dtoa(12.3577) == "12.3577"
assert dtoa(10.0) == "10"
- assert dtoa(1.0e100) == "1" + "0" * 100
+ assert dtoa(1.0e100) == "1e+100"
assert dtoa(rarithmetic.INFINITY) == 'inf'
assert dtoa(-rarithmetic.INFINITY) == '-inf'
assert dtoa(rarithmetic.NAN) == 'nan'
+
+def test_dtoa_precision():
+ assert dtoa(1.1, code='f', precision=2) == "1.10"
More information about the Pypy-commit
mailing list