Author: Armin Rigo <[email protected]>
Branch:
Changeset: r80660:f686da796f5e
Date: 2015-11-13 11:30 +0100
http://bitbucket.org/pypy/pypy/changeset/f686da796f5e/
Log: Merged in yufeiz/pypy-pyos_double_to_string (pull request #357)
Add PyOS_double_to_string and unit tests to cpyext
diff --git a/pypy/module/cpyext/pystrtod.py b/pypy/module/cpyext/pystrtod.py
--- a/pypy/module/cpyext/pystrtod.py
+++ b/pypy/module/cpyext/pystrtod.py
@@ -9,6 +9,18 @@
from rpython.rtyper.lltypesystem import rffi
+# PyOS_double_to_string's "type", if non-NULL, will be set to one of:
+Py_DTST_FINITE = 0
+Py_DTST_INFINITE = 1
+Py_DTST_NAN = 2
+
+# Match the "type" back to values in CPython
+DOUBLE_TO_STRING_TYPES_MAP = {
+ rfloat.DIST_FINITE: Py_DTST_FINITE,
+ rfloat.DIST_INFINITY: Py_DTST_INFINITE,
+ rfloat.DIST_NAN: Py_DTST_NAN
+}
+
@cpython_api([rffi.CCHARP, rffi.CCHARPP, PyObject], rffi.DOUBLE, error=-1.0)
@jit.dont_look_inside # direct use of _get_errno()
def PyOS_string_to_double(space, s, endptr, w_overflow_exception):
@@ -68,3 +80,40 @@
finally:
if not user_endptr:
lltype.free(endptr, flavor='raw')
+
+@cpython_api([rffi.DOUBLE, lltype.Char, rffi.INT_real, rffi.INT_real,
rffi.INTP], rffi.CCHARP)
+def PyOS_double_to_string(space, val, format_code, precision, flags, ptype):
+ """Convert a double val to a string using supplied
+ format_code, precision, and flags.
+
+ format_code must be one of 'e', 'E', 'f', 'F',
+ 'g', 'G' or 'r'. For 'r', the supplied precision
+ must be 0 and is ignored. The 'r' format code specifies the
+ standard repr() format.
+
+ flags can be zero or more of the values Py_DTSF_SIGN,
+ Py_DTSF_ADD_DOT_0, or Py_DTSF_ALT, or-ed together:
+
+ Py_DTSF_SIGN means to always precede the returned string with a sign
+ character, even if val is non-negative.
+
+ Py_DTSF_ADD_DOT_0 means to ensure that the returned string will not look
+ like an integer.
+
+ Py_DTSF_ALT means to apply "alternate" formatting rules. See the
+ documentation for the PyOS_snprintf() '#' specifier for
+ details.
+
+ If ptype is non-NULL, then the value it points to will be set to one of
+ Py_DTST_FINITE, Py_DTST_INFINITE, or Py_DTST_NAN, signifying that
+ val is a finite number, an infinite number, or not a number, respectively.
+
+ The return value is a pointer to buffer with the converted string or
+ NULL if the conversion failed. The caller is responsible for freeing the
+ returned string by calling PyMem_Free().
+ """
+ buffer, rtype = rfloat.double_to_string(val, format_code, precision, flags)
+ if ptype != lltype.nullptr(rffi.INTP.TO):
+ ptype[0] = rffi.cast(rffi.INT, DOUBLE_TO_STRING_TYPES_MAP[rtype])
+ bufp = rffi.str2charp(buffer)
+ return bufp
diff --git a/pypy/module/cpyext/test/test_pystrtod.py
b/pypy/module/cpyext/test/test_pystrtod.py
--- a/pypy/module/cpyext/test/test_pystrtod.py
+++ b/pypy/module/cpyext/test/test_pystrtod.py
@@ -1,5 +1,6 @@
import math
+from pypy.module.cpyext import pystrtod
from pypy.module.cpyext.test.test_api import BaseApiTest
from rpython.rtyper.lltypesystem import rffi
from rpython.rtyper.lltypesystem import lltype
@@ -91,3 +92,76 @@
api.PyErr_Clear()
rffi.free_charp(s)
lltype.free(endp, flavor='raw')
+
+
+class TestPyOS_double_to_string(BaseApiTest):
+
+ def test_format_code(self, api):
+ ptype = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
+ r = api.PyOS_double_to_string(150.0, 'e', 1, 0, ptype)
+ assert '1.5e+02' == rffi.charp2str(r)
+ type_value = rffi.cast(lltype.Signed, ptype[0])
+ assert pystrtod.Py_DTST_FINITE == type_value
+ rffi.free_charp(r)
+ lltype.free(ptype, flavor='raw')
+
+ def test_precision(self, api):
+ ptype = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
+ r = api.PyOS_double_to_string(3.14159269397, 'g', 5, 0, ptype)
+ assert '3.1416' == rffi.charp2str(r)
+ type_value = rffi.cast(lltype.Signed, ptype[0])
+ assert pystrtod.Py_DTST_FINITE == type_value
+ rffi.free_charp(r)
+ lltype.free(ptype, flavor='raw')
+
+ def test_flags_sign(self, api):
+ ptype = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
+ r = api.PyOS_double_to_string(-3.14, 'g', 3, 1, ptype)
+ assert '-3.14' == rffi.charp2str(r)
+ type_value = rffi.cast(lltype.Signed, ptype[0])
+ assert pystrtod.Py_DTST_FINITE == type_value
+ rffi.free_charp(r)
+ lltype.free(ptype, flavor='raw')
+
+ def test_flags_add_dot_0(self, api):
+ ptype = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
+ r = api.PyOS_double_to_string(3, 'g', 5, 2, ptype)
+ assert '3.0' == rffi.charp2str(r)
+ type_value = rffi.cast(lltype.Signed, ptype[0])
+ assert pystrtod.Py_DTST_FINITE == type_value
+ rffi.free_charp(r)
+ lltype.free(ptype, flavor='raw')
+
+ def test_flags_alt(self, api):
+ ptype = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
+ r = api.PyOS_double_to_string(314., 'g', 3, 4, ptype)
+ assert '314.' == rffi.charp2str(r)
+ type_value = rffi.cast(lltype.Signed, ptype[0])
+ assert pystrtod.Py_DTST_FINITE == type_value
+ rffi.free_charp(r)
+ lltype.free(ptype, flavor='raw')
+
+ def test_ptype_nan(self, api):
+ ptype = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
+ r = api.PyOS_double_to_string(float('nan'), 'g', 3, 4, ptype)
+ assert 'nan' == rffi.charp2str(r)
+ type_value = rffi.cast(lltype.Signed, ptype[0])
+ assert pystrtod.Py_DTST_NAN == type_value
+ rffi.free_charp(r)
+ lltype.free(ptype, flavor='raw')
+
+ def test_ptype_infinity(self, api):
+ ptype = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
+ r = api.PyOS_double_to_string(1e200 * 1e200, 'g', 0, 0, ptype)
+ assert 'inf' == rffi.charp2str(r)
+ type_value = rffi.cast(lltype.Signed, ptype[0])
+ assert pystrtod.Py_DTST_INFINITE == type_value
+ rffi.free_charp(r)
+ lltype.free(ptype, flavor='raw')
+
+ def test_ptype_null(self, api):
+ ptype = lltype.nullptr(rffi.INTP.TO)
+ r = api.PyOS_double_to_string(3.14, 'g', 3, 0, ptype)
+ assert '3.14' == rffi.charp2str(r)
+ assert ptype == lltype.nullptr(rffi.INTP.TO)
+ rffi.free_charp(r)
\ No newline at end of file
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit