(code, input, got, expectedback))
test_p_code()
+
+
+###########################################################################
+# SF bug 705836. "<f" and ">f" had a severe rounding bug, where a carry
+# from the low-order discarded bits could propagate into the exponent
+# field, causing the result to be wrong by a factor of 2.
+
+def test_705836():
+ import math
+
+ for base in range(1, 33):
+ # smaller <- largest representable float less than base.
+ delta = 0.5
+ while base - delta / 2.0 != base:
+ delta /= 2.0
+ smaller = base - delta
+ # Packing this rounds away a solid string of trailing 1 bits.
+ packed = struct.pack("<f", smaller)
+ unpacked = struct.unpack("<f", packed)[0]
+ # This failed at base = 2, 4, and 32, with unpacked = 1, 2, and
+ # 16, respectively.
+ verify(base == unpacked)
+ bigpacked = struct.pack(">f", smaller)
+ verify(bigpacked == string_reverse(packed),
+ ">f pack should be byte-reversal of <f pack")
+ unpacked = struct.unpack(">f", bigpacked)[0]
+ verify(base == unpacked)
+
+ # Largest finite IEEE single.
+ big = (1 << 24) - 1
+ big = math.ldexp(big, 127 - 23)
+ packed = struct.pack(">f", big)
+ unpacked = struct.unpack(">f", packed)[0]
+ verify(big == unpacked)
+
+ # The same, but tack on a 1 bit so it rounds up to infinity.
+ big = (1 << 25) - 1
+ big = math.ldexp(big, 127 - 24)
+ try:
+ packed = struct.pack(">f", big)
+ except OverflowError:
+ pass
+ else:
+ TestFailed("expected OverflowError")
+
+test_705836()
Release date: XX-XXX-2003
============================
+- SF #705836: The platform-independent routines for packing floats in
+ IEEE formats (struct.pack's <f, >f, <d, and >d codes; pickle and
+ cPickle's protocol 1 pickling of floats) ignored that rounding can
+ cause a carry to propagate. The worst consequence was that, in rare
+ cases, <f and >f could produce strings that, when unpacked again,
+ were a factor of 2 away from the original float. This has been
+ fixed.
+
- Backported SF patch #676342: after using pdb, the readline command
completion was botched.
value, but according to PEP 237 it really needs to be 1 in Python
2.2.3 and 2.3. (SF #660455)
-- SF bug #678518: fix some bugs in the parser module.
+- SF bug #678518: fix some bugs in the parser module.
- Bastion.py and rexec.py are disabled. These modules are not safe in
Python 2.2. or 2.3.
- SF #570655, fix misleading option text for bdist_rpm
-- Distutils: Allow unknown keyword arguments to the setup() function
+- Distutils: Allow unknown keyword arguments to the setup() function
and the Extension constructor, printing a warning about them instead
of reporting an error and stopping.
gauss() may see different results now.
- The randint() method is rehabilitated (i.e. no longer deprecated).
-
+
- In copy.py: when an object is copied through its __reduce__ method,
there was no check for a __setstate__ method on the result [SF
patch 565085]; deepcopy should treat instances of custom
static int
put(Picklerobject *self, PyObject *ob) {
- if (ob->ob_refcnt < 2 || self->fast)
+ if (ob->ob_refcnt < 2 || self->fast)
return 0;
return put2(self, ob);
return 1;
}
-int
+int
fast_save_leave(Picklerobject *self, PyObject *obj)
{
if (self->fast_container-- >= PY_CPICKLE_FAST_LIMIT) {
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);
/* 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);
}
return 0;
+
+ Overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "float too large to pack with d format");
+ return -1;
}
static PyObject *
Pickle_clear_memo(Picklerobject *self, PyObject *args) {
- if (!PyArg_ParseTuple(args,":clear_memo"))
+ if (!PyArg_ParseTuple(args,":clear_memo"))
return NULL;
- if (self->memo)
+ if (self->memo)
PyDict_Clear(self->memo);
Py_INCREF(Py_None);
return Py_None;
Pdata *data;
/* Can be called by Python code or C code */
- if (args && !PyArg_ParseTuple(args, "|i:getvalue", &clear))
+ if (args && !PyArg_ParseTuple(args, "|i:getvalue", &clear))
return NULL;
/* Check to make sure we are based on a list */
};
static PyGetSetDef Pickler_getsets[] = {
- {"persistent_id", (getter)Pickler_get_pers_func,
+ {"persistent_id", (getter)Pickler_get_pers_func,
(setter)Pickler_set_pers_func},
{"inst_persistent_id", NULL, (setter)Pickler_set_inst_pers_func},
{"memo", (getter)Pickler_get_memo, (setter)Pickler_set_memo},
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);
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);
/* Done */
return 0;
+
+ Overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "float too large to pack with f format");
+ return -1;
}
static int
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);
/* 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);
/* Done */
return 0;
+
+ Overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "float too large to pack with d format");
+ return -1;
}
static PyObject *