From 58cbdbb8ef356f1f17a7ef9e896fcc439ce8eb08 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 19 Sep 2020 11:15:55 -0700 Subject: [PATCH 1/5] Revert "bpo-41513: Remove broken tests that fail on Gentoo (GH-22249)" This reverts commit 95a8a0e01d1f4f49ad6b4d4f2ae3bbf93c462013. --- Lib/test/test_math.py | 51 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index f5283c5e0dcb63..0c9984ff7ce13b 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -803,6 +803,57 @@ def testHypot(self): scale = FLOAT_MIN / 2.0 ** exp self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) + def testHypotAccuracy(self): + # Verify improved accuracy in cases that were known to be inaccurate. + + hypot = math.hypot + Decimal = decimal.Decimal + high_precision = decimal.Context(prec=500) + + for hx, hy in [ + # Cases with a 1 ulp error in Python 3.7 compiled with Clang + ('0x1.10e89518dca48p+29', '0x1.1970f7565b7efp+30'), + ('0x1.10106eb4b44a2p+29', '0x1.ef0596cdc97f8p+29'), + ('0x1.459c058e20bb7p+30', '0x1.993ca009b9178p+29'), + ('0x1.378371ae67c0cp+30', '0x1.fbe6619854b4cp+29'), + ('0x1.f4cd0574fb97ap+29', '0x1.50fe31669340ep+30'), + ('0x1.494b2cdd3d446p+29', '0x1.212a5367b4c7cp+29'), + ('0x1.f84e649f1e46dp+29', '0x1.1fa56bef8eec4p+30'), + ('0x1.2e817edd3d6fap+30', '0x1.eb0814f1e9602p+29'), + ('0x1.0d3a6e3d04245p+29', '0x1.32a62fea52352p+30'), + ('0x1.888e19611bfc5p+29', '0x1.52b8e70b24353p+29'), + + # Cases with 2 ulp error in Python 3.8 + ('0x1.538816d48a13fp+29', '0x1.7967c5ca43e16p+29'), + ('0x1.57b47b7234530p+29', '0x1.74e2c7040e772p+29'), + ('0x1.821b685e9b168p+30', '0x1.677dc1c1e3dc6p+29'), + ('0x1.9e8247f67097bp+29', '0x1.24bd2dc4f4baep+29'), + ('0x1.b73b59e0cb5f9p+29', '0x1.da899ab784a97p+28'), + ('0x1.94a8d2842a7cfp+30', '0x1.326a51d4d8d8ap+30'), + ('0x1.e930b9cd99035p+29', '0x1.5a1030e18dff9p+30'), + ('0x1.1592bbb0e4690p+29', '0x1.a9c337b33fb9ap+29'), + ('0x1.1243a50751fd4p+29', '0x1.a5a10175622d9p+29'), + ('0x1.57a8596e74722p+30', '0x1.42d1af9d04da9p+30'), + + # Cases with 1 ulp error in version fff3c28052e6b0750d6218e00acacd2fded4991a + ('0x1.ee7dbd9565899p+29', '0x1.7ab4d6fc6e4b4p+29'), + ('0x1.5c6bfbec5c4dcp+30', '0x1.02511184b4970p+30'), + ('0x1.59dcebba995cap+30', '0x1.50ca7e7c38854p+29'), + ('0x1.768cdd94cf5aap+29', '0x1.9cfdc5571d38ep+29'), + ('0x1.dcf137d60262ep+29', '0x1.1101621990b3ep+30'), + ('0x1.3a2d006e288b0p+30', '0x1.e9a240914326cp+29'), + ('0x1.62a32f7f53c61p+29', '0x1.47eb6cd72684fp+29'), + ('0x1.d3bcb60748ef2p+29', '0x1.3f13c4056312cp+30'), + ('0x1.282bdb82f17f3p+30', '0x1.640ba4c4eed3ap+30'), + ('0x1.89d8c423ea0c6p+29', '0x1.d35dcfe902bc3p+29'), + ]: + with self.subTest(hx=hx, hy=hy): + x = float.fromhex(hx) + y = float.fromhex(hy) + with decimal.localcontext(high_precision): + z = float((Decimal(x)**2 + Decimal(y)**2).sqrt()) + self.assertEqual(hypot(x, y), z) + def testDist(self): from decimal import Decimal as D from fractions import Fraction as F From 5edc606e113acae1c7d37343cead8aa0e33aa43e Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 19 Sep 2020 11:30:40 -0700 Subject: [PATCH 2/5] Add guards to bypass the test when double rounding can occur. --- Lib/test/test_math.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 0c9984ff7ce13b..0e26f5fcaf8775 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -803,8 +803,20 @@ def testHypot(self): scale = FLOAT_MIN / 2.0 ** exp self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) + @requires_IEEE_754 + @unittest.skipIf(HAVE_DOUBLE_ROUNDING, + "hypot() loses accuracy on machines with double rounding") def testHypotAccuracy(self): # Verify improved accuracy in cases that were known to be inaccurate. + # + # The new algorithm's accuracy depends on IEEE 754 arithmetic + # guarantees, on having the usual ROUND HALF EVEN rounding mode, on + # the system not having double rounding due to extended precision, + # and on the compiler maintaining the specified order of operations. + # + # This test is known to succeed on most of our builds. If it fails + # some build, we either need to add another skipIf if the cause is + # identifiable; otherwise, we can remove this test entirely. hypot = math.hypot Decimal = decimal.Decimal @@ -835,7 +847,7 @@ def testHypotAccuracy(self): ('0x1.1243a50751fd4p+29', '0x1.a5a10175622d9p+29'), ('0x1.57a8596e74722p+30', '0x1.42d1af9d04da9p+30'), - # Cases with 1 ulp error in version fff3c28052e6b0750d6218e00acacd2fded4991a + # Cases with 1 ulp error in version fff3c28052e6b0 ('0x1.ee7dbd9565899p+29', '0x1.7ab4d6fc6e4b4p+29'), ('0x1.5c6bfbec5c4dcp+30', '0x1.02511184b4970p+30'), ('0x1.59dcebba995cap+30', '0x1.50ca7e7c38854p+29'), From 9255115bf17cb2efa1a2b06532c77a317b56d1b4 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 20 Sep 2020 14:46:24 -0700 Subject: [PATCH 3/5] Check Gentoo bot to determine which guard caused the tests to pass --- Lib/test/test_math.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 0e26f5fcaf8775..bd8a7212abc84e 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -803,7 +803,7 @@ def testHypot(self): scale = FLOAT_MIN / 2.0 ** exp self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) - @requires_IEEE_754 + #@requires_IEEE_754 @unittest.skipIf(HAVE_DOUBLE_ROUNDING, "hypot() loses accuracy on machines with double rounding") def testHypotAccuracy(self): From 6da426df6232052774eafc67dcbe262c4162eeaa Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 20 Sep 2020 17:13:41 -0700 Subject: [PATCH 4/5] Restore the requirement for IEEE_754 --- Lib/test/test_math.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index bd8a7212abc84e..0e26f5fcaf8775 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -803,7 +803,7 @@ def testHypot(self): scale = FLOAT_MIN / 2.0 ** exp self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale) - #@requires_IEEE_754 + @requires_IEEE_754 @unittest.skipIf(HAVE_DOUBLE_ROUNDING, "hypot() loses accuracy on machines with double rounding") def testHypotAccuracy(self): From 1cd8b1334d58aff2c351aa1b4ffa9fcc2217cbc9 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 20 Sep 2020 20:10:01 -0700 Subject: [PATCH 5/5] Generate more informative error messages --- Lib/test/test_math.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 0e26f5fcaf8775..2abe5b028b355d 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -859,9 +859,9 @@ def testHypotAccuracy(self): ('0x1.282bdb82f17f3p+30', '0x1.640ba4c4eed3ap+30'), ('0x1.89d8c423ea0c6p+29', '0x1.d35dcfe902bc3p+29'), ]: - with self.subTest(hx=hx, hy=hy): - x = float.fromhex(hx) - y = float.fromhex(hy) + x = float.fromhex(hx) + y = float.fromhex(hy) + with self.subTest(hx=hx, hy=hy, x=x, y=y): with decimal.localcontext(high_precision): z = float((Decimal(x)**2 + Decimal(y)**2).sqrt()) self.assertEqual(hypot(x, y), z)