mirror of
https://github.com/python/cpython.git
synced 2025-08-10 11:58:39 +00:00
[3.14] gh-132876: workaround broken ldexp() on Windows 10 (GH-133135) (#134684)
gh-132876: workaround broken ldexp() on Windows 10 (GH-133135)
* 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 cf8941c603
)
Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
Co-authored-by: Tim Peters <tim.peters@gmail.com>
This commit is contained in:
parent
7216f7b59c
commit
16187b58bf
3 changed files with 31 additions and 0 deletions
|
@ -1214,6 +1214,12 @@ class MathTests(unittest.TestCase):
|
||||||
self.assertEqual(math.ldexp(NINF, n), NINF)
|
self.assertEqual(math.ldexp(NINF, n), NINF)
|
||||||
self.assertTrue(math.isnan(math.ldexp(NAN, n)))
|
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):
|
def testLog(self):
|
||||||
self.assertRaises(TypeError, math.log)
|
self.assertRaises(TypeError, math.log)
|
||||||
self.assertRaises(TypeError, math.log, 1, 2, 3)
|
self.assertRaises(TypeError, math.log, 1, 2, 3)
|
||||||
|
|
|
@ -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.
|
|
@ -2161,6 +2161,27 @@ math_ldexp_impl(PyObject *module, double x, PyObject *i)
|
||||||
} else {
|
} else {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
r = ldexp(x, (int)exp);
|
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 (isinf(r))
|
if (isinf(r))
|
||||||
errno = ERANGE;
|
errno = ERANGE;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue