Commit 21492573 authored by Christian Heimes's avatar Christian Heimes

Issue #1580: New free format floating point representation based on...

Issue #1580: New free format floating point representation based on "Floating-Point Printer Sample Code", by Robert G. Burger. For example repr(11./5) now returns '2.2' instead of '2.2000000000000002'.

Thanks to noam for the patch! I had to modify doubledigits.c slightly to support X64 and IA64 machines on Windows. I also added the new file to the three project files.
parent 505769d7
...@@ -76,6 +76,10 @@ PyAPI_FUNC(int) _PyFloat_Pack8(double x, unsigned char *p, int le); ...@@ -76,6 +76,10 @@ PyAPI_FUNC(int) _PyFloat_Pack8(double x, unsigned char *p, int le);
*/ */
PyAPI_FUNC(int) _PyFloat_Repr(double x, char *p, size_t len); PyAPI_FUNC(int) _PyFloat_Repr(double x, char *p, size_t len);
/* Used to get the important decimal digits of a double */
PyAPI_FUNC(int) _PyFloat_Digits(char *buf, double v, int *signum);
PyAPI_FUNC(void) _PyFloat_DigitsInit(void);
/* The unpack routines read 4 or 8 bytes, starting at p. le is a bool /* The unpack routines read 4 or 8 bytes, starting at p. le is a bool
* argument, true if the string is in little-endian format (exponent * argument, true if the string is in little-endian format (exponent
* last, at p+3 or p+7), false if big-endian (exponent first, at p). * last, at p+3 or p+7), false if big-endian (exponent first, at p).
......
import unittest, struct import unittest, struct
import os
from test import test_support from test import test_support
class FormatFunctionsTestCase(unittest.TestCase): class FormatFunctionsTestCase(unittest.TestCase):
...@@ -146,12 +147,26 @@ class FormatTestCase(unittest.TestCase): ...@@ -146,12 +147,26 @@ class FormatTestCase(unittest.TestCase):
self.assertRaises(ValueError, format, 3.0, "s") self.assertRaises(ValueError, format, 3.0, "s")
class ReprTestCase(unittest.TestCase):
def test_repr(self):
floats_file = open(os.path.join(os.path.split(__file__)[0],
'floating_points.txt'))
for line in floats_file:
line = line.strip()
if not line or line.startswith('#'):
continue
v = eval(line)
self.assertEqual(v, eval(repr(v)))
floats_file.close()
def test_main(): def test_main():
test_support.run_unittest( test_support.run_unittest(
FormatFunctionsTestCase, FormatFunctionsTestCase,
UnknownFormatTestCase, UnknownFormatTestCase,
IEEEFormatTestCase, IEEEFormatTestCase,
FormatTestCase) FormatTestCase,
ReprTestCase)
if __name__ == '__main__': if __name__ == '__main__':
test_main() test_main()
...@@ -301,6 +301,7 @@ OBJECT_OBJS= \ ...@@ -301,6 +301,7 @@ OBJECT_OBJS= \
Objects/genobject.o \ Objects/genobject.o \
Objects/fileobject.o \ Objects/fileobject.o \
Objects/floatobject.o \ Objects/floatobject.o \
Objects/doubledigits.o \
Objects/frameobject.o \ Objects/frameobject.o \
Objects/funcobject.o \ Objects/funcobject.o \
Objects/iterobject.o \ Objects/iterobject.o \
......
...@@ -12,6 +12,10 @@ What's New in Python 3.0a3? ...@@ -12,6 +12,10 @@ What's New in Python 3.0a3?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #1580: New free format floating point representation based on
"Floating-Point Printer Sample Code", by Robert G. Burger. For example
repr(11./5) now returns '2.2' instead of '2.2000000000000002'.
- Issue #1573: Improper use of the keyword-only syntax makes the parser crash - Issue #1573: Improper use of the keyword-only syntax makes the parser crash
- Issue #1564: The set implementation should special-case PyUnicode instead - Issue #1564: The set implementation should special-case PyUnicode instead
......
This diff is collapsed.
...@@ -281,6 +281,107 @@ format_float(char *buf, size_t buflen, PyFloatObject *v, int precision) ...@@ -281,6 +281,107 @@ format_float(char *buf, size_t buflen, PyFloatObject *v, int precision)
format_double(buf, buflen, PyFloat_AS_DOUBLE(v), precision); format_double(buf, buflen, PyFloat_AS_DOUBLE(v), precision);
} }
/* The following function is based on Tcl_PrintDouble,
* from tclUtil.c.
*/
#define is_infinite(d) ( (d) > DBL_MAX || (d) < -DBL_MAX )
#define is_nan(d) ((d) != (d))
static void
format_double_repr(char *dst, double value)
{
char *p, c;
int exp;
int signum;
char buffer[30];
/*
* Handle NaN.
*/
if (is_nan(value)) {
strcpy(dst, "nan");
return;
}
/*
* Handle infinities.
*/
if (is_infinite(value)) {
if (value < 0) {
strcpy(dst, "-inf");
} else {
strcpy(dst, "inf");
}
return;
}
/*
* Ordinary (normal and denormal) values.
*/
exp = _PyFloat_Digits(buffer, value, &signum)+1;
if (signum) {
*dst++ = '-';
}
p = buffer;
if (exp < -3 || exp > 17) {
/*
* E format for numbers < 1e-3 or >= 1e17.
*/
*dst++ = *p++;
c = *p;
if (c != '\0') {
*dst++ = '.';
while (c != '\0') {
*dst++ = c;
c = *++p;
}
}
sprintf(dst, "e%+d", exp-1);
} else {
/*
* F format for others.
*/
if (exp <= 0) {
*dst++ = '0';
}
c = *p;
while (exp-- > 0) {
if (c != '\0') {
*dst++ = c;
c = *++p;
} else {
*dst++ = '0';
}
}
*dst++ = '.';
if (c == '\0') {
*dst++ = '0';
} else {
while (++exp < 0) {
*dst++ = '0';
}
while (c != '\0') {
*dst++ = c;
c = *++p;
}
}
*dst++ = '\0';
}
}
static void
format_float_repr(char *buf, PyFloatObject *v)
{
assert(PyFloat_Check(v));
format_double_repr(buf, PyFloat_AS_DOUBLE(v));
}
/* Macro and helper that convert PyObject obj to a C double and store /* Macro and helper that convert PyObject obj to a C double and store
the value in dbl. If conversion to double raises an exception, obj is the value in dbl. If conversion to double raises an exception, obj is
set to NULL, and the function invoking this macro returns NULL. If set to NULL, and the function invoking this macro returns NULL. If
...@@ -333,8 +434,8 @@ convert_to_double(PyObject **v, double *dbl) ...@@ -333,8 +434,8 @@ convert_to_double(PyObject **v, double *dbl)
static PyObject * static PyObject *
float_repr(PyFloatObject *v) float_repr(PyFloatObject *v)
{ {
char buf[100]; char buf[30];
format_float(buf, sizeof(buf), v, PREC_REPR); format_float_repr(buf, v);
return PyUnicode_FromString(buf); return PyUnicode_FromString(buf);
} }
...@@ -1226,6 +1327,9 @@ _PyFloat_Init(void) ...@@ -1226,6 +1327,9 @@ _PyFloat_Init(void)
double_format = detected_double_format; double_format = detected_double_format;
float_format = detected_float_format; float_format = detected_float_format;
/* Initialize floating point repr */
_PyFloat_DigitsInit();
} }
void void
......
...@@ -488,6 +488,9 @@ ...@@ -488,6 +488,9 @@
RelativePath="..\Objects\dictobject.c"> RelativePath="..\Objects\dictobject.c">
</File> </File>
<File <File
RelativePath="..\Objects\doubledigits.c">
</File>
<File
RelativePath="..\PC\dl_nt.c"> RelativePath="..\PC\dl_nt.c">
</File> </File>
<File <File
......
...@@ -827,6 +827,10 @@ ...@@ -827,6 +827,10 @@
RelativePath="..\..\Objects\dictobject.c" RelativePath="..\..\Objects\dictobject.c"
> >
</File> </File>
<File
RelativePath="..\..\Objects\doubledigits.c"
>
</File>
<File <File
RelativePath="..\..\Objects\memoryobject.c" RelativePath="..\..\Objects\memoryobject.c"
> >
......
...@@ -1362,6 +1362,10 @@ ...@@ -1362,6 +1362,10 @@
RelativePath="..\Objects\dictobject.c" RelativePath="..\Objects\dictobject.c"
> >
</File> </File>
<File
RelativePath="..\Objects\doubledigits.c"
>
</File>
<File <File
RelativePath="..\Objects\enumobject.c" RelativePath="..\Objects\enumobject.c"
> >
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment