mirror of
https://github.com/python/cpython.git
synced 2025-07-24 11:44:31 +00:00
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.
This commit is contained in:
parent
62364ffb80
commit
d50ade68ec
4 changed files with 115 additions and 18 deletions
|
@ -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 *
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue