From 617180e45bde3d375d173eeac218dff096d5ce8e Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Thu, 19 Aug 2021 14:57:04 +0100 Subject: [PATCH 1/4] bpo-44954: Fix wrong result in float.fromhex corner case --- Lib/test/test_float.py | 10 ++++++++++ .../2021-08-19-14-43-24.bpo-44954.dLn3lg.rst | 2 ++ Objects/floatobject.c | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-08-19-14-43-24.bpo-44954.dLn3lg.rst diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 38a17cedd6446f..c41a97f5bf0d7c 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -1468,6 +1468,16 @@ def test_from_hex(self): self.identical(fromHex('0X1.0000000000001fp0'), 1.0+2*EPS) self.identical(fromHex('0x1.00000000000020p0'), 1.0+2*EPS) + # Regression test for a corner-case bug reported in b.p.o. 44954 + self.identical(fromHex('0x.8p-1074'), 0.0) + self.identical(fromHex('0x.80p-1074'), 0.0) + self.identical(fromHex('0x.81p-1074'), TINY) + self.identical(fromHex('0x8p-1078'), 0.0) + self.identical(fromHex('0x8.0p-1078'), 0.0) + self.identical(fromHex('0x8.1p-1078'), TINY) + self.identical(fromHex('0x80p-1082'), 0.0) + self.identical(fromHex('0x81p-1082'), TINY) + def test_roundtrip(self): def roundtrip(x): return fromHex(toHex(x)) diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-08-19-14-43-24.bpo-44954.dLn3lg.rst b/Misc/NEWS.d/next/Core and Builtins/2021-08-19-14-43-24.bpo-44954.dLn3lg.rst new file mode 100644 index 00000000000000..4cdeb34b8b6116 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-08-19-14-43-24.bpo-44954.dLn3lg.rst @@ -0,0 +1,2 @@ +Fixed a corner case bug where the result of ``float.fromhex('0x.8p-1074')`` +was rounded the wrong way. diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 7e78132c01ca27..6853554e0a1e70 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -1463,8 +1463,8 @@ float_fromhex(PyTypeObject *type, PyObject *string) bits lsb, lsb-2, lsb-3, lsb-4, ... is 1. */ if ((digit & half_eps) != 0) { round_up = 0; - if ((digit & (3*half_eps-1)) != 0 || - (half_eps == 8 && (HEX_DIGIT(key_digit+1) & 1) != 0)) + if ((digit & (3*half_eps-1)) != 0 || (half_eps == 8 && + key_digit + 1 < ndigits && (HEX_DIGIT(key_digit+1) & 1) != 0)) round_up = 1; else for (i = key_digit-1; i >= 0; i--) From dfb3fefef0e5540ecd9d43bc05691947a6e8551b Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Fri, 20 Aug 2021 08:34:10 +0100 Subject: [PATCH 2/4] A few more test variants --- Lib/test/test_float.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index c41a97f5bf0d7c..cde9ae1ea624a3 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -1477,6 +1477,10 @@ def test_from_hex(self): self.identical(fromHex('0x8.1p-1078'), TINY) self.identical(fromHex('0x80p-1082'), 0.0) self.identical(fromHex('0x81p-1082'), TINY) + self.identical(fromHex('.8p-1074'), 0.0) + self.identical(fromHex('8p-1078'), 0.0) + self.identical(fromHex('-.8p-1074'), 0.0) + self.identical(fromHex('+8p-1078'), 0.0) def test_roundtrip(self): def roundtrip(x): From 45cd7ddfdde120ee9bfd470d6fdb0d2b95106c63 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Fri, 20 Aug 2021 08:35:11 +0100 Subject: [PATCH 3/4] Fix spacing consistency --- Objects/floatobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 6853554e0a1e70..92faa7c1320237 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -1464,7 +1464,7 @@ float_fromhex(PyTypeObject *type, PyObject *string) if ((digit & half_eps) != 0) { round_up = 0; if ((digit & (3*half_eps-1)) != 0 || (half_eps == 8 && - key_digit + 1 < ndigits && (HEX_DIGIT(key_digit+1) & 1) != 0)) + key_digit+1 < ndigits && (HEX_DIGIT(key_digit+1) & 1) != 0)) round_up = 1; else for (i = key_digit-1; i >= 0; i--) From 2fed72cabc1d6a2744141645eeb4c7c749b59fbd Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Fri, 20 Aug 2021 09:01:31 +0100 Subject: [PATCH 4/4] Fix a bad test --- Lib/test/test_float.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index cde9ae1ea624a3..29f775644dd4a8 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -1479,7 +1479,7 @@ def test_from_hex(self): self.identical(fromHex('0x81p-1082'), TINY) self.identical(fromHex('.8p-1074'), 0.0) self.identical(fromHex('8p-1078'), 0.0) - self.identical(fromHex('-.8p-1074'), 0.0) + self.identical(fromHex('-.8p-1074'), -0.0) self.identical(fromHex('+8p-1078'), 0.0) def test_roundtrip(self):