diff Modules/structmodule.c @ 28333:eec3fd164a7d 2.2

SF bug 705836: struct.pack of floats in non-native endian order pack_float, pack_double, save_float: All the routines for creating IEEE-format packed representations of floats and doubles simply ignored that rounding can (in rare cases) propagate out of a long string of 1 bits. At worst, the end-off carry can (by mistake) interfere with the exponent value, and then unpacking yields a result wrong by a factor of 2. In less severe cases, it can end up losing more low-order bits than intended, or fail to catch overflow *caused* by rounding.
author Tim Peters <tim.peters@gmail.com>
date Thu, 20 Mar 2003 18:31:20 +0000
parents 0b68d810a8be
children
line wrap: on
line diff
--- a/Modules/structmodule.c	Thu Mar 20 18:22:50 2003 +0000
+++ b/Modules/structmodule.c	Thu Mar 20 18:31:20 2003 +0000
@@ -225,12 +225,8 @@
 		return -1;
 	}
 
-	if (e >= 128) {
-		/* XXX 128 itself is reserved for Inf/NaN */
-		PyErr_SetString(PyExc_OverflowError,
-				"float too large to pack with f format");
-		return -1;
-	}
+	if (e >= 128)
+		goto Overflow;
 	else if (e < -126) {
 		/* Gradual underflow */
 		f = ldexp(f, 126 + e);
@@ -243,6 +239,14 @@
 
 	f *= 8388608.0; /* 2**23 */
 	fbits = (long) floor(f + 0.5); /* Round */
+	assert(fbits <= 8388608);
+	if (fbits >> 23) {
+		/* The carry propagated out of a string of 23 1 bits. */
+		fbits = 0;
+		++e;
+		if (e >= 255)
+			goto Overflow;
+	}
 
 	/* First byte */
 	*p = (s<<7) | (e>>1);
@@ -261,6 +265,11 @@
 
 	/* Done */
 	return 0;
+
+ Overflow:
+	PyErr_SetString(PyExc_OverflowError,
+			"float too large to pack with f format");
+	return -1;
 }
 
 static int
@@ -296,12 +305,8 @@
 		return -1;
 	}
 
-	if (e >= 1024) {
-		/* XXX 1024 itself is reserved for Inf/NaN */
-		PyErr_SetString(PyExc_OverflowError,
-				"float too large to pack with d format");
-		return -1;
-	}
+	if (e >= 1024)
+		goto Overflow;
 	else if (e < -1022) {
 		/* Gradual underflow */
 		f = ldexp(f, 1022 + e);
@@ -315,9 +320,24 @@
 	/* fhi receives the high 28 bits; flo the low 24 bits (== 52 bits) */
 	f *= 268435456.0; /* 2**28 */
 	fhi = (long) floor(f); /* Truncate */
+	assert(fhi < 268435456);
+
 	f -= (double)fhi;
 	f *= 16777216.0; /* 2**24 */
 	flo = (long) floor(f + 0.5); /* Round */
+	assert(flo <= 16777216);
+	if (flo >> 24) {
+		/* The carry propagated out of a string of 24 1 bits. */
+		flo = 0;
+		++fhi;
+		if (fhi >> 28) {
+			/* And it also progagated out of the next 28 bits. */
+			fhi = 0;
+			++e;
+			if (e >= 2047)
+				goto Overflow;
+		}
+	}
 
 	/* First byte */
 	*p = (s<<7) | (e>>4);
@@ -353,6 +373,11 @@
 
 	/* Done */
 	return 0;
+
+ Overflow:
+	PyErr_SetString(PyExc_OverflowError,
+			"float too large to pack with d format");
+	return -1;
 }
 
 static PyObject *