[Python-checkins] r59459 - in python/trunk: Include/floatobject.h Lib/test/floating_points.txt Lib/test/test_float.py Makefile.pre.in Misc/NEWS Objects/doubledigits.c Objects/floatobject.c PCbuild/pythoncore.vcproj PCbuild8/pythoncore/pythoncore.vcproj PCbuild9/pythoncore.vcproj

christian.heimes python-checkins at python.org
Mon Dec 10 23:28:56 CET 2007


Author: christian.heimes
Date: Mon Dec 10 23:28:56 2007
New Revision: 59459

Added:
   python/trunk/Lib/test/floating_points.txt
      - copied unchanged from r59458, python/branches/py3k/Lib/test/floating_points.txt
   python/trunk/Objects/doubledigits.c
      - copied unchanged from r59457, python/branches/py3k/Objects/doubledigits.c
Modified:
   python/trunk/Include/floatobject.h
   python/trunk/Lib/test/test_float.py
   python/trunk/Makefile.pre.in
   python/trunk/Misc/NEWS
   python/trunk/Objects/floatobject.c
   python/trunk/PCbuild/pythoncore.vcproj
   python/trunk/PCbuild8/pythoncore/pythoncore.vcproj
   python/trunk/PCbuild9/pythoncore.vcproj
Log:
Backport of r59456:59458 from py3k to trunk
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.

Modified: python/trunk/Include/floatobject.h
==============================================================================
--- python/trunk/Include/floatobject.h	(original)
+++ python/trunk/Include/floatobject.h	Mon Dec 10 23:28:56 2007
@@ -86,6 +86,10 @@
 PyAPI_FUNC(int) _PyFloat_Pack4(double x, unsigned char *p, int le);
 PyAPI_FUNC(int) _PyFloat_Pack8(double x, unsigned char *p, int le);
 
+/* 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
  * 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).

Modified: python/trunk/Lib/test/test_float.py
==============================================================================
--- python/trunk/Lib/test/test_float.py	(original)
+++ python/trunk/Lib/test/test_float.py	Mon Dec 10 23:28:56 2007
@@ -1,5 +1,6 @@
 
 import unittest, struct
+import os
 from test import test_support
 
 class FormatFunctionsTestCase(unittest.TestCase):
@@ -115,11 +116,25 @@
             self.assertEquals(pos_neg(), neg_neg())
 
 
+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():
     test_support.run_unittest(
         FormatFunctionsTestCase,
         UnknownFormatTestCase,
-        IEEEFormatTestCase)
+        IEEEFormatTestCase,
+        ReprTestCase)
 
 if __name__ == '__main__':
     test_main()

Modified: python/trunk/Makefile.pre.in
==============================================================================
--- python/trunk/Makefile.pre.in	(original)
+++ python/trunk/Makefile.pre.in	Mon Dec 10 23:28:56 2007
@@ -299,6 +299,7 @@
 		Objects/genobject.o \
 		Objects/fileobject.o \
 		Objects/floatobject.o \
+		Objects/doubledigits.o \
 		Objects/frameobject.o \
 		Objects/funcobject.o \
 		Objects/intobject.o \

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Mon Dec 10 23:28:56 2007
@@ -12,6 +12,10 @@
 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 #1538: Avoid copying string in split/rsplit if the split
   char is not found.
 

Modified: python/trunk/Objects/floatobject.c
==============================================================================
--- python/trunk/Objects/floatobject.c	(original)
+++ python/trunk/Objects/floatobject.c	Mon Dec 10 23:28:56 2007
@@ -306,6 +306,107 @@
 	format_float(buf, 100, 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
    the value in dbl; this replaces the functionality of the coercion
    slot function.  If conversion to double raises an exception, obj is
@@ -390,8 +491,8 @@
 static PyObject *
 float_repr(PyFloatObject *v)
 {
-	char buf[100];
-	format_float(buf, sizeof(buf), v, PREC_REPR);
+	char buf[30];
+	format_float_repr(buf, v);
 	return PyString_FromString(buf);
 }
 
@@ -1290,6 +1391,9 @@
 
 	double_format = detected_double_format;
 	float_format = detected_float_format;
+	
+	/* Initialize floating point repr */
+	_PyFloat_DigitsInit();
 }
 
 void

Modified: python/trunk/PCbuild/pythoncore.vcproj
==============================================================================
--- python/trunk/PCbuild/pythoncore.vcproj	(original)
+++ python/trunk/PCbuild/pythoncore.vcproj	Mon Dec 10 23:28:56 2007
@@ -485,6 +485,9 @@
 			RelativePath="..\Objects\dictobject.c">
 		</File>
 		<File
+			RelativePath="..\Objects\doubledigits.c">
+		</File>
+        <File
 			RelativePath="..\PC\dl_nt.c">
 		</File>
 		<File

Modified: python/trunk/PCbuild8/pythoncore/pythoncore.vcproj
==============================================================================
--- python/trunk/PCbuild8/pythoncore/pythoncore.vcproj	(original)
+++ python/trunk/PCbuild8/pythoncore/pythoncore.vcproj	Mon Dec 10 23:28:56 2007
@@ -820,6 +820,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\Objects\doubledigits.c"
+				>
+			</File>
+			<File
 				RelativePath="..\..\Objects\enumobject.c"
 				>
 			</File>

Modified: python/trunk/PCbuild9/pythoncore.vcproj
==============================================================================
--- python/trunk/PCbuild9/pythoncore.vcproj	(original)
+++ python/trunk/PCbuild9/pythoncore.vcproj	Mon Dec 10 23:28:56 2007
@@ -1371,6 +1371,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\Objects\doubledigits.c"
+				>
+			</File>
+			<File
 				RelativePath="..\Objects\enumobject.c"
 				>
 			</File>


More information about the Python-checkins mailing list