summaryrefslogtreecommitdiffstats
path: root/Modules/structmodule.c
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2003-03-20 18:32:13 (GMT)
committerTim Peters <tim.peters@gmail.com>2003-03-20 18:32:13 (GMT)
commitd50ade68ec240ea8ea12604809d8c70985263dce (patch)
tree7dd938bc5a9dcf40707839e518420804bbb3cd94 /Modules/structmodule.c
parent62364ffb80a7dab0477fe7dc7aec5478ab6afec2 (diff)
downloadcpython-d50ade68ec240ea8ea12604809d8c70985263dce.zip
cpython-d50ade68ec240ea8ea12604809d8c70985263dce.tar.gz
cpython-d50ade68ec240ea8ea12604809d8c70985263dce.tar.bz2
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. Bugfix candidate, but I already backported this to 2.2. In 2.3, this code remains in severe need of refactoring.
Diffstat (limited to 'Modules/structmodule.c')
-rw-r--r--Modules/structmodule.c49
1 files changed, 37 insertions, 12 deletions
diff --git a/Modules/structmodule.c b/Modules/structmodule.c
index d4f8d86..2210c33 100644
--- a/Modules/structmodule.c
+++ b/Modules/structmodule.c
@@ -224,12 +224,8 @@ pack_float(double x, /* The number to pack */
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);
@@ -242,6 +238,14 @@ pack_float(double x, /* The number to pack */
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);
@@ -260,6 +264,11 @@ pack_float(double x, /* The number to pack */
/* Done */
return 0;
+
+ Overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "float too large to pack with f format");
+ return -1;
}
static int
@@ -295,12 +304,8 @@ pack_double(double x, /* The number to pack */
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);
@@ -314,9 +319,24 @@ pack_double(double x, /* The number to pack */
/* 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);
@@ -352,6 +372,11 @@ pack_double(double x, /* The number to pack */
/* Done */
return 0;
+
+ Overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "float too large to pack with d format");
+ return -1;
}
static PyObject *