mirror of
https://github.com/python/cpython.git
synced 2025-08-30 13:38:43 +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
|
@ -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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue