[Python-checkins] bpo-39396: Fix math.nextafter(-0.0, +0.0) on AIX 7.1 (GH-18094)

Victor Stinner webhook-mailer at python.org
Tue Jan 21 05:14:18 EST 2020


https://github.com/python/cpython/commit/85ead4fc62829cb7ef2eb0af1a2933282f58c629
commit: 85ead4fc62829cb7ef2eb0af1a2933282f58c629
branch: master
author: Victor Stinner <vstinner at python.org>
committer: GitHub <noreply at github.com>
date: 2020-01-21T11:14:10+01:00
summary:

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.

files:
A Misc/NEWS.d/next/Library/2020-01-21-09-00-42.bpo-39396.6UXQXE.rst
M Lib/test/test_math.py
M Modules/mathmodule.c

diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py
index 6d10227a0c135..e96fd745970a1 100644
--- a/Lib/test/test_math.py
+++ b/Lib/test/test_math.py
@@ -1752,6 +1752,83 @@ def assertIsNaN(self, value):
         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 @@ def testComb(self):
             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 0000000000000..af7076854a5f9
--- /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 81d871786f139..f012b51d86698 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));
 }
 
 



More information about the Python-checkins mailing list