From 85ead4fc62829cb7ef2eb0af1a2933282f58c629 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 21 Jan 2020 11:14:10 +0100 Subject: [PATCH] bpo-39396: Fix math.nextafter(-0.0, +0.0) on AIX 7.1 (GH-18094) Move also math.nextafter() on math.ulp() tests from IsCloseTests to MathTests. --- Lib/test/test_math.py | 154 +++++++++--------- .../2020-01-21-09-00-42.bpo-39396.6UXQXE.rst | 1 + Modules/mathmodule.c | 10 +- 3 files changed, 86 insertions(+), 79 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-01-21-09-00-42.bpo-39396.6UXQXE.rst diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 6d10227a0c1..e96fd745970 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1752,6 +1752,83 @@ class MathTests(unittest.TestCase): if not math.isnan(value): self.fail("Expected a NaN, got {!r}.".format(value)) + def assertEqualSign(self, x, y): + """Similar to assertEqual(), but compare also the sign. + + Function useful to compare signed zeros. + """ + self.assertEqual(x, y) + self.assertEqual(math.copysign(1.0, x), math.copysign(1.0, y)) + + @requires_IEEE_754 + def test_nextafter(self): + # around 2^52 and 2^63 + self.assertEqual(math.nextafter(4503599627370496.0, -INF), + 4503599627370495.5) + self.assertEqual(math.nextafter(4503599627370496.0, INF), + 4503599627370497.0) + self.assertEqual(math.nextafter(9223372036854775808.0, 0.0), + 9223372036854774784.0) + self.assertEqual(math.nextafter(-9223372036854775808.0, 0.0), + -9223372036854774784.0) + + # around 1.0 + self.assertEqual(math.nextafter(1.0, -INF), + float.fromhex('0x1.fffffffffffffp-1')) + self.assertEqual(math.nextafter(1.0, INF), + float.fromhex('0x1.0000000000001p+0')) + + # x == y: y is returned + self.assertEqual(math.nextafter(2.0, 2.0), 2.0) + self.assertEqualSign(math.nextafter(-0.0, +0.0), +0.0) + self.assertEqualSign(math.nextafter(+0.0, -0.0), -0.0) + + # around 0.0 + smallest_subnormal = sys.float_info.min * sys.float_info.epsilon + self.assertEqual(math.nextafter(+0.0, INF), smallest_subnormal) + self.assertEqual(math.nextafter(-0.0, INF), smallest_subnormal) + self.assertEqual(math.nextafter(+0.0, -INF), -smallest_subnormal) + self.assertEqual(math.nextafter(-0.0, -INF), -smallest_subnormal) + self.assertEqualSign(math.nextafter(smallest_subnormal, +0.0), +0.0) + self.assertEqualSign(math.nextafter(-smallest_subnormal, +0.0), -0.0) + self.assertEqualSign(math.nextafter(smallest_subnormal, -0.0), +0.0) + self.assertEqualSign(math.nextafter(-smallest_subnormal, -0.0), -0.0) + + # around infinity + largest_normal = sys.float_info.max + self.assertEqual(math.nextafter(INF, 0.0), largest_normal) + self.assertEqual(math.nextafter(-INF, 0.0), -largest_normal) + self.assertEqual(math.nextafter(largest_normal, INF), INF) + self.assertEqual(math.nextafter(-largest_normal, -INF), -INF) + + # NaN + self.assertTrue(math.isnan(math.nextafter(NAN, 1.0))) + self.assertTrue(math.isnan(math.nextafter(1.0, NAN))) + self.assertTrue(math.isnan(math.nextafter(NAN, NAN))) + + @requires_IEEE_754 + def test_ulp(self): + self.assertEqual(math.ulp(1.0), sys.float_info.epsilon) + # use int ** int rather than float ** int to not rely on pow() accuracy + self.assertEqual(math.ulp(2 ** 52), 1.0) + self.assertEqual(math.ulp(2 ** 53), 2.0) + self.assertEqual(math.ulp(2 ** 64), 4096.0) + + # min and max + self.assertEqual(math.ulp(0.0), + sys.float_info.min * sys.float_info.epsilon) + self.assertEqual(math.ulp(FLOAT_MAX), + FLOAT_MAX - math.nextafter(FLOAT_MAX, -INF)) + + # special cases + self.assertEqual(math.ulp(INF), INF) + self.assertTrue(math.isnan(math.ulp(math.nan))) + + # negative number: ulp(-x) == ulp(x) + for x in (0.0, 1.0, 2 ** 52, 2 ** 64, INF): + with self.subTest(x=x): + self.assertEqual(math.ulp(-x), math.ulp(x)) + class IsCloseTests(unittest.TestCase): isclose = math.isclose # subclasses should override this @@ -2009,83 +2086,6 @@ class IsCloseTests(unittest.TestCase): self.assertIs(type(comb(IntSubclass(5), IntSubclass(k))), int) self.assertIs(type(comb(MyIndexable(5), MyIndexable(k))), int) - def assertEqualSign(self, x, y): - """Similar to assertEqual(), but compare also the sign. - - Function useful to compare signed zeros. - """ - self.assertEqual(x, y) - self.assertEqual(math.copysign(1.0, x), math.copysign(1.0, y)) - - @requires_IEEE_754 - def test_nextafter(self): - # around 2^52 and 2^63 - self.assertEqual(math.nextafter(4503599627370496.0, -INF), - 4503599627370495.5) - self.assertEqual(math.nextafter(4503599627370496.0, INF), - 4503599627370497.0) - self.assertEqual(math.nextafter(9223372036854775808.0, 0.0), - 9223372036854774784.0) - self.assertEqual(math.nextafter(-9223372036854775808.0, 0.0), - -9223372036854774784.0) - - # around 1.0 - self.assertEqual(math.nextafter(1.0, -INF), - float.fromhex('0x1.fffffffffffffp-1')) - self.assertEqual(math.nextafter(1.0, INF), - float.fromhex('0x1.0000000000001p+0')) - - # x == y: y is returned - self.assertEqual(math.nextafter(2.0, 2.0), 2.0) - self.assertEqualSign(math.nextafter(-0.0, +0.0), +0.0) - self.assertEqualSign(math.nextafter(+0.0, -0.0), -0.0) - - # around 0.0 - smallest_subnormal = sys.float_info.min * sys.float_info.epsilon - self.assertEqual(math.nextafter(+0.0, INF), smallest_subnormal) - self.assertEqual(math.nextafter(-0.0, INF), smallest_subnormal) - self.assertEqual(math.nextafter(+0.0, -INF), -smallest_subnormal) - self.assertEqual(math.nextafter(-0.0, -INF), -smallest_subnormal) - self.assertEqualSign(math.nextafter(smallest_subnormal, +0.0), +0.0) - self.assertEqualSign(math.nextafter(-smallest_subnormal, +0.0), -0.0) - self.assertEqualSign(math.nextafter(smallest_subnormal, -0.0), +0.0) - self.assertEqualSign(math.nextafter(-smallest_subnormal, -0.0), -0.0) - - # around infinity - largest_normal = sys.float_info.max - self.assertEqual(math.nextafter(INF, 0.0), largest_normal) - self.assertEqual(math.nextafter(-INF, 0.0), -largest_normal) - self.assertEqual(math.nextafter(largest_normal, INF), INF) - self.assertEqual(math.nextafter(-largest_normal, -INF), -INF) - - # NaN - self.assertTrue(math.isnan(math.nextafter(NAN, 1.0))) - self.assertTrue(math.isnan(math.nextafter(1.0, NAN))) - self.assertTrue(math.isnan(math.nextafter(NAN, NAN))) - - @requires_IEEE_754 - def test_ulp(self): - self.assertEqual(math.ulp(1.0), sys.float_info.epsilon) - # use int ** int rather than float ** int to not rely on pow() accuracy - self.assertEqual(math.ulp(2 ** 52), 1.0) - self.assertEqual(math.ulp(2 ** 53), 2.0) - self.assertEqual(math.ulp(2 ** 64), 4096.0) - - # min and max - self.assertEqual(math.ulp(0.0), - sys.float_info.min * sys.float_info.epsilon) - self.assertEqual(math.ulp(FLOAT_MAX), - FLOAT_MAX - math.nextafter(FLOAT_MAX, -INF)) - - # special cases - self.assertEqual(math.ulp(INF), INF) - self.assertTrue(math.isnan(math.ulp(math.nan))) - - # negative number: ulp(-x) == ulp(x) - for x in (0.0, 1.0, 2 ** 52, 2 ** 64, INF): - with self.subTest(x=x): - self.assertEqual(math.ulp(-x), math.ulp(x)) - def test_main(): from doctest import DocFileSuite diff --git a/Misc/NEWS.d/next/Library/2020-01-21-09-00-42.bpo-39396.6UXQXE.rst b/Misc/NEWS.d/next/Library/2020-01-21-09-00-42.bpo-39396.6UXQXE.rst new file mode 100644 index 00000000000..af7076854a5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-01-21-09-00-42.bpo-39396.6UXQXE.rst @@ -0,0 +1 @@ +Fix ``math.nextafter(-0.0, +0.0)`` on AIX 7.1. diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 81d871786f1..f012b51d866 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -3287,8 +3287,14 @@ static PyObject * math_nextafter_impl(PyObject *module, double x, double y) /*[clinic end generated code: output=750c8266c1c540ce input=02b2d50cd1d9f9b6]*/ { - double f = nextafter(x, y); - return PyFloat_FromDouble(f); +#if defined(_AIX) + if (x == y) { + /* On AIX 7.1, libm nextafter(-0.0, +0.0) returns -0.0. + Bug fixed in bos.adt.libm 7.2.2.0 by APAR IV95512. */ + return PyFloat_FromDouble(y); + } +#endif + return PyFloat_FromDouble(nextafter(x, y)); }