Skip to content

Commit 812276c

Browse files
skirpichevtim-one
andcommitted
gh-132876: workaround broken ldexp() on Windows 10
Co-authored-by: Tim Peters <[email protected]>
1 parent 219b1f9 commit 812276c

File tree

2 files changed

+21
-0
lines changed

2 files changed

+21
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Workaround broken ``ldexp()`` implementation on Windows 10.

Modules/mathmodule.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2163,6 +2163,26 @@ math_ldexp_impl(PyObject *module, double x, PyObject *i)
21632163
} else {
21642164
errno = 0;
21652165
r = ldexp(x, (int)exp);
2166+
#if _MSC_VER
2167+
if (r && r > -DBL_MIN && r < DBL_MIN) {
2168+
/* Denormal result can be incorrectly rounded here (rather,
2169+
truncated). Fixed in newer versions of the C runtime, included
2170+
with Windows 11. */
2171+
int original_exp;
2172+
frexp(x, &original_exp);
2173+
if (original_exp > DBL_MIN_EXP) {
2174+
/* Shift down to the smallest normal binade. No bits lost. */
2175+
x = ldexp(x, DBL_MIN_EXP - original_exp);
2176+
exp += original_exp - DBL_MIN_EXP;
2177+
}
2178+
/* Multiplying by 2**exp finishes the job, and the HW will round as
2179+
appropriate. Note: if exp < -DBL_MANT_DIG, all of x is shifted
2180+
to be < 0.5ULP of smallest denorm, so should be thrown away. If
2181+
exp is so very negative that ldexp underflows to 0, that's fine;
2182+
no need to check in advance. */
2183+
r = x*ldexp(1.0, (int)exp);
2184+
}
2185+
#endif
21662186
if (isinf(r))
21672187
errno = ERANGE;
21682188
}

0 commit comments

Comments
 (0)