https://github.com/python/cpython/commit/e483dcf19eee7143ba76d1829afc1052053a904e
commit: e483dcf19eee7143ba76d1829afc1052053a904e
branch: 3.13
author: Sergey B Kirpichev <skirpic...@gmail.com>
committer: tim-one <tim.pet...@gmail.com>
date: 2025-05-25T22:39:34-05:00
summary:

[3.13] gh-132876: workaround broken ldexp() on Windows 10 (GH-133135) (#134685)

* gh-132876: workaround broken ldexp() on Windows 10

ldexp() fails to round subnormal results before Windows 11,
so hide their bug.
(cherry picked from commit cf8941c60356acdd00055e5583a2d64761c34af4)

Co-authored-by: Tim Peters <tim.pet...@gmail.com>

files:
A Misc/NEWS.d/next/Library/2025-04-29-11-48-46.gh-issue-132876.lyTQGZ.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 d309e8c1c41f18..5ee3055c871419 100644
--- a/Lib/test/test_math.py
+++ b/Lib/test/test_math.py
@@ -1202,6 +1202,12 @@ def testLdexp(self):
             self.assertEqual(math.ldexp(NINF, n), NINF)
             self.assertTrue(math.isnan(math.ldexp(NAN, n)))
 
+    @requires_IEEE_754
+    def testLdexp_denormal(self):
+        # Denormal output incorrectly rounded (truncated)
+        # on some Windows.
+        self.assertEqual(math.ldexp(6993274598585239, -1126), 1e-323)
+
     def testLog(self):
         self.assertRaises(TypeError, math.log)
         self.assertRaises(TypeError, math.log, 1, 2, 3)
diff --git 
a/Misc/NEWS.d/next/Library/2025-04-29-11-48-46.gh-issue-132876.lyTQGZ.rst 
b/Misc/NEWS.d/next/Library/2025-04-29-11-48-46.gh-issue-132876.lyTQGZ.rst
new file mode 100644
index 00000000000000..cb3ca3321e3d26
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-04-29-11-48-46.gh-issue-132876.lyTQGZ.rst
@@ -0,0 +1,4 @@
+``ldexp()`` on Windows doesn't round subnormal results before Windows 11,
+but should.  Python's :func:`math.ldexp` wrapper now does round them, so
+results may change slightly, in rare cases of very small results, on
+Windows versions before 11.
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index 2d56dc35731c0b..aee1b17be9cb2a 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -2166,6 +2166,27 @@ math_ldexp_impl(PyObject *module, double x, PyObject *i)
     } else {
         errno = 0;
         r = ldexp(x, (int)exp);
+#ifdef _MSC_VER
+        if (DBL_MIN > r && r > -DBL_MIN) {
+            /* Denormal (or zero) results can be incorrectly rounded here 
(rather,
+               truncated).  Fixed in newer versions of the C runtime, included
+               with Windows 11. */
+            int original_exp;
+            frexp(x, &original_exp);
+            if (original_exp > DBL_MIN_EXP) {
+                /* Shift down to the smallest normal binade.  No bits lost. */
+                int shift = DBL_MIN_EXP - original_exp;
+                x = ldexp(x, shift);
+                exp -= shift;
+            }
+            /* Multiplying by 2**exp finishes the job, and the HW will round as
+               appropriate.  Note: if exp < -DBL_MANT_DIG, all of x is shifted
+               to be < 0.5ULP of smallest denorm, so should be thrown away.  If
+               exp is so very negative that ldexp underflows to 0, that's fine;
+               no need to check in advance. */
+            r = x*ldexp(1.0, (int)exp);
+        }
+#endif
         if (Py_IS_INFINITY(r))
             errno = ERANGE;
     }

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to