Skip to content

Commit 81db22f

Browse files
stinosdpgeorge
authored andcommitted
py/modmath: Work around msvc float bugs in atan2, fmod and modf.
Older implementations deal with infinity/negative zero incorrectly. This commit adds generic fixes that can be enabled by any port that needs them, along with new tests cases.
1 parent a902b69 commit 81db22f

File tree

4 files changed

+51
-3
lines changed

4 files changed

+51
-3
lines changed

ports/windows/mpconfigport.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,14 @@ extern const struct _mp_obj_module_t mp_module_time;
227227
#define MP_SSIZE_MAX _I32_MAX
228228
#endif
229229

230+
// VC++ 12.0 fixes
231+
#if (_MSC_VER <= 1800)
232+
#define MICROPY_PY_MATH_ATAN2_FIX_INFNAN (1)
233+
#define MICROPY_PY_MATH_FMOD_FIX_INFNAN (1)
234+
#ifdef _WIN64
235+
#define MICROPY_PY_MATH_MODF_FIX_NEGZERO (1)
236+
#endif
237+
#endif
230238

231239
// CL specific definitions
232240

py/modmath.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
// M_PI is not part of the math.h standard and may not be defined
3535
// And by defining our own we can ensure it uses the correct const format.
3636
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
37+
#define MP_PI_4 MICROPY_FLOAT_CONST(0.78539816339744830962)
38+
#define MP_3_PI_4 MICROPY_FLOAT_CONST(2.35619449019234492885)
3739

3840
STATIC NORETURN void math_error(void) {
3941
mp_raise_ValueError(MP_ERROR_TEXT("math domain error"));
@@ -132,7 +134,17 @@ MATH_FUN_1(asin, asin)
132134
// atan(x)
133135
MATH_FUN_1(atan, atan)
134136
// atan2(y, x)
137+
#if MICROPY_PY_MATH_ATAN2_FIX_INFNAN
138+
mp_float_t atan2_func(mp_float_t x, mp_float_t y) {
139+
if (isinf(x) && isinf(y)) {
140+
return copysign(y < 0 ? MP_3_PI_4 : MP_PI_4, x);
141+
}
142+
return atan2(x, y);
143+
}
144+
MATH_FUN_2(atan2, atan2_func)
145+
#else
135146
MATH_FUN_2(atan2, atan2)
147+
#endif
136148
// ceil(x)
137149
MATH_FUN_1_TO_INT(ceil, ceil)
138150
// copysign(x, y)
@@ -148,7 +160,14 @@ MATH_FUN_1(fabs, fabs_func)
148160
// floor(x)
149161
MATH_FUN_1_TO_INT(floor, floor) // TODO: delegate to x.__floor__() if x is not a float
150162
// fmod(x, y)
163+
#if MICROPY_PY_MATH_FMOD_FIX_INFNAN
164+
mp_float_t fmod_func(mp_float_t x, mp_float_t y) {
165+
return (!isinf(x) && isinf(y)) ? x : fmod(x, y);
166+
}
167+
MATH_FUN_2(fmod, fmod_func)
168+
#else
151169
MATH_FUN_2(fmod, fmod)
170+
#endif
152171
// isfinite(x)
153172
MATH_FUN_1_TO_BOOL(isfinite, isfinite)
154173
// isinf(x)
@@ -246,7 +265,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_frexp_obj, mp_math_frexp);
246265
// modf(x)
247266
STATIC mp_obj_t mp_math_modf(mp_obj_t x_obj) {
248267
mp_float_t int_part = 0.0;
249-
mp_float_t fractional_part = MICROPY_FLOAT_C_FUN(modf)(mp_obj_get_float(x_obj), &int_part);
268+
mp_float_t x = mp_obj_get_float(x_obj);
269+
mp_float_t fractional_part = MICROPY_FLOAT_C_FUN(modf)(x, &int_part);
270+
#if MICROPY_PY_MATH_MODF_FIX_NEGZERO
271+
if (fractional_part == MICROPY_FLOAT_CONST(0.0)) {
272+
fractional_part = copysign(fractional_part, x);
273+
}
274+
#endif
250275
mp_obj_t tuple[2];
251276
tuple[0] = mp_obj_new_float(fractional_part);
252277
tuple[1] = mp_obj_new_float(int_part);

py/mpconfig.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,21 @@ typedef double mp_float_t;
11281128
#define MICROPY_PY_MATH_ISCLOSE (0)
11291129
#endif
11301130

1131+
// Whether to provide fix for atan2 Inf handling.
1132+
#ifndef MICROPY_PY_MATH_ATAN2_FIX_INFNAN
1133+
#define MICROPY_PY_MATH_ATAN2_FIX_INFNAN (0)
1134+
#endif
1135+
1136+
// Whether to provide fix for fmod Inf handling.
1137+
#ifndef MICROPY_PY_MATH_FMOD_FIX_INFNAN
1138+
#define MICROPY_PY_MATH_FMOD_FIX_INFNAN (0)
1139+
#endif
1140+
1141+
// Whether to provide fix for modf negative zero handling.
1142+
#ifndef MICROPY_PY_MATH_MODF_FIX_NEGZERO
1143+
#define MICROPY_PY_MATH_MODF_FIX_NEGZERO (0)
1144+
#endif
1145+
11311146
// Whether to provide "cmath" module
11321147
#ifndef MICROPY_PY_CMATH
11331148
#define MICROPY_PY_CMATH (0)

tests/float/math_domain.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
# double argument functions
4040
for name, f, args in (
4141
("pow", math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3))),
42-
("fmod", math.fmod, ((1.2, inf), (1.2, 0), (inf, 1.2))),
43-
("atan2", math.atan2, ((0, 0),)),
42+
("fmod", math.fmod, ((1.2, inf), (1.2, -inf), (1.2, 0), (inf, 1.2))),
43+
("atan2", math.atan2, ((0, 0), (-inf, inf), (-inf, -inf), (inf, -inf))),
4444
("copysign", math.copysign, ()),
4545
):
4646
for x in args + ((0, inf), (inf, 0), (inf, inf), (inf, nan), (nan, inf), (nan, nan)):

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy