From 20d41f1182647256310a18a4ec79c1bdb82050be Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 12 Mar 2025 08:10:12 +0100 Subject: [PATCH 1/4] gh-131032: Fix math.fma(x, y, z) zero sign Fix result sign when z is zero. Co-Authored-by: Sergey B Kirpichev --- Lib/test/test_math.py | 6 ------ Modules/mathmodule.c | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index ba94a29800c871..5f8f9e75a44d3c 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -2765,12 +2765,6 @@ def test_fma_infinities(self): self.assertEqual(math.fma(-b, -math.inf, c), math.inf) self.assertEqual(math.fma(-b, math.inf, c), -math.inf) - # gh-73468: On some platforms, libc fma() doesn't implement IEE 754-2008 - # properly: it doesn't use the right sign when the result is zero. - @unittest.skipIf( - sys.platform.startswith(("freebsd", "wasi", "netbsd", "emscripten")) - or (sys.platform == "android" and platform.machine() == "x86_64"), - f"this platform doesn't implement IEE 754-2008 properly") def test_fma_zero_result(self): nonnegative_finites = [0.0, 1e-300, 2.3, 1e300] diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index b4c15a143f9838..a1906c9051e037 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2344,7 +2344,22 @@ static PyObject * math_fma_impl(PyObject *module, double x, double y, double z) /*[clinic end generated code: output=4fc8626dbc278d17 input=e3ad1f4a4c89626e]*/ { - double r = fma(x, y, z); + double r; + if (z) { + r = fma(x, y, z); + } + else { + // gh-73468, gh-131032: On some platforms (ex: WASI, NetBSD, + // Emscripten, musl C library), libc fma() doesn't implement + // IEEE 754-2008 properly: it doesn't use the right sign when the + // result is zero. + if (x && y) { + r = x * y; + } + else { + r = copysign(1, z) == 1 ? x*y + z : x*y; + } + } /* Fast path: if we got a finite result, we're done. */ if (isfinite(r)) { From c95e6f939193cd9b0095f4dce75b55ea15cbb386 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 12 Mar 2025 08:14:55 +0100 Subject: [PATCH 2/4] Add NEWS entry --- .../next/Library/2025-03-12-08-14-54.gh-issue-131032.kpSzWI.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-03-12-08-14-54.gh-issue-131032.kpSzWI.rst diff --git a/Misc/NEWS.d/next/Library/2025-03-12-08-14-54.gh-issue-131032.kpSzWI.rst b/Misc/NEWS.d/next/Library/2025-03-12-08-14-54.gh-issue-131032.kpSzWI.rst new file mode 100644 index 00000000000000..19644ec3f5b6b1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-12-08-14-54.gh-issue-131032.kpSzWI.rst @@ -0,0 +1,2 @@ +Fix :func:`math.fma(x, y, z) ` zero sign: fix the result sign when +*z* is zero. Patch by Victor Stinner and Sergey B Kirpichev. From 7dba0229ec00ada5e3a0790f556c472c616a1f4b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 12 Mar 2025 12:15:51 +0100 Subject: [PATCH 3/4] Add test_fma_zero_result2() --- Lib/test/test_math.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 5f8f9e75a44d3c..ae3f5af0d47834 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -2806,6 +2806,13 @@ def test_fma_zero_result(self): self.assertIsPositiveZero(math.fma(-tiny, -tiny, -0.0)) self.assertIsNegativeZero(math.fma(-tiny, tiny, -0.0)) + # gh-73468: On some platforms, libc fma() doesn't implement IEE 754-2008 + # properly: it doesn't use the right sign when the result is zero. + @unittest.skipIf( + sys.platform.startswith(("freebsd", "netbsd", "emscripten")) + or (sys.platform == "android" and platform.machine() == "x86_64"), + f"this platform doesn't implement IEE 754-2008 properly") + def test_fma_zero_result2(self): # Corner case where rounding the multiplication would # give the wrong result. x = float.fromhex('0x1p-500') From 4e7c5abe6767ec3ee7be0804091640a98aa825b9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 12 Mar 2025 12:20:05 +0100 Subject: [PATCH 4/4] Change coding style --- Modules/mathmodule.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index a1906c9051e037..58fb04c268f5fc 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -2353,11 +2353,9 @@ math_fma_impl(PyObject *module, double x, double y, double z) // Emscripten, musl C library), libc fma() doesn't implement // IEEE 754-2008 properly: it doesn't use the right sign when the // result is zero. - if (x && y) { - r = x * y; - } - else { - r = copysign(1, z) == 1 ? x*y + z : x*y; + r = x * y; + if ((!x || !y) && copysign(1, z) == 1) { + r += z; } }