diff options
author | Tim Peters <tim.peters@gmail.com> | 2003-03-20 18:32:13 (GMT) |
---|---|---|
committer | Tim Peters <tim.peters@gmail.com> | 2003-03-20 18:32:13 (GMT) |
commit | d50ade68ec240ea8ea12604809d8c70985263dce (patch) | |
tree | 7dd938bc5a9dcf40707839e518420804bbb3cd94 /Lib/test/test_struct.py | |
parent | 62364ffb80a7dab0477fe7dc7aec5478ab6afec2 (diff) | |
download | cpython-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.py | 46 |
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() |