Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix denormal double parsing #135

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 38 additions & 47 deletions peachpy/literal.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,27 +407,25 @@ def _parse_float32(number):
is_negative = number.startswith("-")
point_position = number.index('.')
exp_position = number.rindex('p')
exponent = int(number[exp_position + 1:])
number_prefix = number[int(is_negative):point_position]
assert number_prefix == '0x0' or number_prefix == '0x1'
mantissa = number[point_position + 1:exp_position]
if number_prefix == '0x0' and int(mantissa) == 0:
# Zero
return int(is_negative) << 31
fraction_str = number[point_position + 1:exp_position]
shifted_sign = int(is_negative) << 31
if number_prefix == '0x0':
# Zero, or a float64 denormal that will be zero as a float32
assert exponent == 0 if int(fraction_str, 16) == 0 else exponent == -1022
return shifted_sign
assert number_prefix == '0x1'
mantissa_bits = len(fraction_str) * 4
if mantissa_bits < 23:
mantissa = int(fraction_str, 16) << (23 - mantissa_bits)
else:
exponent = number[exp_position + 1:]
mantissa_bits = len(mantissa) * 4
if mantissa_bits == 23:
mantissa = int(mantissa, 16)
elif mantissa_bits < 23:
mantissa = int(mantissa, 16) << (23 - mantissa_bits)
else:
mantissa = int(mantissa, 16) >> (mantissa_bits - 23)
exponent = int(exponent)
if exponent <= -127:
# Denormals
mantissa = (mantissa + (1 << 23)) >> -(exponent + 126)
exponent = -127
return mantissa + (int(exponent + 127) << 23) + (int(is_negative) << 31)
mantissa = int(fraction_str, 16) >> (mantissa_bits - 23)
if exponent <= -127:
# Denormals
mantissa = (mantissa + (1 << 23)) >> -(exponent + 126)
return mantissa + shifted_sign
return mantissa + (exponent + 127 << 23) + shifted_sign

@staticmethod
def _parse_float64(number):
Expand All @@ -438,7 +436,9 @@ def _parse_float64(number):
try:
number = float.hex(float.fromhex(number))
except ValueError:
raise ValueError("The string %s is not a hexadecimal floating-point number" % number)
raise ValueError(
"The string %s is not a hexadecimal floating-point number" % number
)
else:
raise TypeError("Unsupported type of constant number %s" % str(number))
if number == "inf" or number == "+inf":
Expand All @@ -448,31 +448,22 @@ def _parse_float64(number):
if number == "nan":
return 0x7FF8000000000000
is_negative = number.startswith("-")
point_position = number.index('.')
exp_position = number.rindex('p')
number_prefix = number[int(is_negative):point_position]
assert number_prefix == '0x0' or number_prefix == '0x1'
mantissa = number[point_position + 1:exp_position]
if number_prefix == '0x0':
# Zero
assert int(mantissa) == 0
return int(is_negative) << 63
point_position = number.index(".")
exp_position = number.rindex("p")
exponent = int(number[exp_position + 1 :])
assert -1022 <= exponent <= 1023
number_prefix = number[int(is_negative) : point_position]
fraction_str = number[point_position + 1 : exp_position]
mantissa_bits = len(fraction_str) * 4
shifted_sign = int(is_negative) << 63
if mantissa_bits == 52:
mantissa = int(fraction_str, 16)
else:
exponent = number[exp_position + 1:]
mantissa_bits = len(mantissa) * 4
if mantissa_bits == 52:
mantissa = int(mantissa, 16)
elif mantissa_bits < 52:
mantissa = int(mantissa, 16) << (52 - mantissa_bits)
else:
mantissa = int(mantissa, 16) >> (mantissa_bits - 52)
exponent = int(exponent)
if exponent <= -1023:
# Denormals
mantissa = (mantissa + (1 << 52)) >> -(exponent + 1022)
exponent = -1023
elif exponent > 1023:
# Infinity
mantissa = 0
exponent = 1023
return mantissa + (int(exponent + 1023) << 52) + (int(is_negative) << 63)
assert mantissa_bits < 52
mantissa = int(fraction_str, 16) << (52 - mantissa_bits)
if number_prefix == "0x0":
# Either Zero or a Denormal
assert exponent == 0 and mantissa == 0 or exponent == -1022 and mantissa != 0
return mantissa + shifted_sign
assert number_prefix == "0x1"
return mantissa + (exponent + 1023 << 52) + shifted_sign
23 changes: 22 additions & 1 deletion tests/test_literal.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,22 @@ def runTest(self):
self.assertEqual(Constant.float64(-0.75).data, (0xBFE8000000000000,))
self.assertEqual(Constant.float64(-2.0).data, (0xC000000000000000,))
self.assertEqual(Constant.float64(-float("inf")).data, (0xFFF0000000000000,))
self.assertEqual(Constant.float64(1.23).data, (0x3FF3AE147AE147AE,))
self.assertEqual(Constant.float64("0x1.6A09E667F3BCDp+0").data, (0x3FF6A09E667F3BCD,))
self.assertEqual(Constant.float64("0x1.BB67AE8584CAAp+0").data, (0x3FFBB67AE8584CAA,))
self.assertEqual(Constant.float64("0x1.921fb54442d18p+1").data, (0x400921FB54442D18,))
self.assertEqual(Constant.float64("0x1.5bf0a8b145769p+1").data, (0x4005BF0A8B145769,))

# absolutely largest positive (and negative) normalzed single float
self.assertEqual(Constant.float64("0x1.0000000000000p-1022").data, (0x0010000000000000,))
self.assertEqual(Constant.float64("-0x1.0000000000000p-1022").data, (0x8010000000000000,))
# absolutely largest positive (and negative) denormalized single float
self.assertEqual(Constant.float64("0x0.fffffffffffffp-1022").data, (0x000FFFFFFFFFFFFF,))
self.assertEqual(Constant.float64("-0x0.fffffffffffffp-1022").data, (0x800FFFFFFFFFFFFF,))
# absolutely smallest positive (and negative) denormalized single float
self.assertEqual(Constant.float64("0x0.0000000000001p-1022").data, (0x0000000000000001,))
self.assertEqual(Constant.float64("-0x0.0000000000001p-1022").data, (0x8000000000000001,))


class Float32(unittest.TestCase):
def runTest(self):
Expand All @@ -95,8 +106,18 @@ def runTest(self):
self.assertEqual(Constant.float32(-0.5).data, (0xBF000000,))
self.assertEqual(Constant.float32(-0.75).data, (0xBF400000,))
self.assertEqual(Constant.float32(-2.0).data, (0xC0000000,))
self.assertEqual(Constant.float32(1.23).data, (0x3f9d70a4,))
self.assertEqual(Constant.float32(-float("inf")).data, (0xFF800000,))
self.assertEqual(Constant.float32("0x1.6A09E6p+0").data, (0x3FB504F3,))
self.assertEqual(Constant.float32("0x1.BB67AEp+0").data, (0x3FDDB3D7,))
self.assertEqual(Constant.float32("0x1.921FB6p+1").data, (0x40490FDB,))
self.assertEqual(Constant.float32("0x1.5BF0A8p+1").data, (0x402DF854,))
self.assertEqual(Constant.float32("0x1.5BF0A8p+1").data, (0x402DF854,))
# absolutely largest positive (and negative) normalzed single float
self.assertEqual(Constant.float32("0x1.000000p-126").data, (0x00800000,))
self.assertEqual(Constant.float32("-0x1.000000p-126").data, (0x80800000,))
# absolutely largest positive (and negative) denormalized single float
self.assertEqual(Constant.float32("0x0.fffffep-126").data, (0x007fffff,))
self.assertEqual(Constant.float32("-0x0.fffffep-126").data, (0x807fffff,))
# absolutely smallest positive (and negative) denormalized single float
self.assertEqual(Constant.float32("0x0.000002p-126").data, (0x00000001,))
self.assertEqual(Constant.float32("-0x0.000002p-126").data, (0x80000001,))