summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_struct.py
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 /Lib/test/test_struct.py
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 'Lib/test/test_struct.py')
-rw-r--r--Lib/test/test_struct.py46
1 files changed, 46 insertions, 0 deletions
diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py
index bc0ace8..1e1092d 100644
--- a/Lib/test/test_struct.py
+++ b/Lib/test/test_struct.py
@@ -390,3 +390,49 @@ def test_p_code():
(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()