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.
This commit is contained in:
parent
a902b69dd5
commit
81db22f693
@ -227,6 +227,14 @@ extern const struct _mp_obj_module_t mp_module_time;
|
||||
#define MP_SSIZE_MAX _I32_MAX
|
||||
#endif
|
||||
|
||||
// VC++ 12.0 fixes
|
||||
#if (_MSC_VER <= 1800)
|
||||
#define MICROPY_PY_MATH_ATAN2_FIX_INFNAN (1)
|
||||
#define MICROPY_PY_MATH_FMOD_FIX_INFNAN (1)
|
||||
#ifdef _WIN64
|
||||
#define MICROPY_PY_MATH_MODF_FIX_NEGZERO (1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// CL specific definitions
|
||||
|
||||
|
27
py/modmath.c
27
py/modmath.c
@ -34,6 +34,8 @@
|
||||
// M_PI is not part of the math.h standard and may not be defined
|
||||
// And by defining our own we can ensure it uses the correct const format.
|
||||
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
|
||||
#define MP_PI_4 MICROPY_FLOAT_CONST(0.78539816339744830962)
|
||||
#define MP_3_PI_4 MICROPY_FLOAT_CONST(2.35619449019234492885)
|
||||
|
||||
STATIC NORETURN void math_error(void) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("math domain error"));
|
||||
@ -132,7 +134,17 @@ MATH_FUN_1(asin, asin)
|
||||
// atan(x)
|
||||
MATH_FUN_1(atan, atan)
|
||||
// atan2(y, x)
|
||||
#if MICROPY_PY_MATH_ATAN2_FIX_INFNAN
|
||||
mp_float_t atan2_func(mp_float_t x, mp_float_t y) {
|
||||
if (isinf(x) && isinf(y)) {
|
||||
return copysign(y < 0 ? MP_3_PI_4 : MP_PI_4, x);
|
||||
}
|
||||
return atan2(x, y);
|
||||
}
|
||||
MATH_FUN_2(atan2, atan2_func)
|
||||
#else
|
||||
MATH_FUN_2(atan2, atan2)
|
||||
#endif
|
||||
// ceil(x)
|
||||
MATH_FUN_1_TO_INT(ceil, ceil)
|
||||
// copysign(x, y)
|
||||
@ -148,7 +160,14 @@ MATH_FUN_1(fabs, fabs_func)
|
||||
// floor(x)
|
||||
MATH_FUN_1_TO_INT(floor, floor) // TODO: delegate to x.__floor__() if x is not a float
|
||||
// fmod(x, y)
|
||||
#if MICROPY_PY_MATH_FMOD_FIX_INFNAN
|
||||
mp_float_t fmod_func(mp_float_t x, mp_float_t y) {
|
||||
return (!isinf(x) && isinf(y)) ? x : fmod(x, y);
|
||||
}
|
||||
MATH_FUN_2(fmod, fmod_func)
|
||||
#else
|
||||
MATH_FUN_2(fmod, fmod)
|
||||
#endif
|
||||
// isfinite(x)
|
||||
MATH_FUN_1_TO_BOOL(isfinite, isfinite)
|
||||
// isinf(x)
|
||||
@ -246,7 +265,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_frexp_obj, mp_math_frexp);
|
||||
// modf(x)
|
||||
STATIC mp_obj_t mp_math_modf(mp_obj_t x_obj) {
|
||||
mp_float_t int_part = 0.0;
|
||||
mp_float_t fractional_part = MICROPY_FLOAT_C_FUN(modf)(mp_obj_get_float(x_obj), &int_part);
|
||||
mp_float_t x = mp_obj_get_float(x_obj);
|
||||
mp_float_t fractional_part = MICROPY_FLOAT_C_FUN(modf)(x, &int_part);
|
||||
#if MICROPY_PY_MATH_MODF_FIX_NEGZERO
|
||||
if (fractional_part == MICROPY_FLOAT_CONST(0.0)) {
|
||||
fractional_part = copysign(fractional_part, x);
|
||||
}
|
||||
#endif
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = mp_obj_new_float(fractional_part);
|
||||
tuple[1] = mp_obj_new_float(int_part);
|
||||
|
@ -1128,6 +1128,21 @@ typedef double mp_float_t;
|
||||
#define MICROPY_PY_MATH_ISCLOSE (0)
|
||||
#endif
|
||||
|
||||
// Whether to provide fix for atan2 Inf handling.
|
||||
#ifndef MICROPY_PY_MATH_ATAN2_FIX_INFNAN
|
||||
#define MICROPY_PY_MATH_ATAN2_FIX_INFNAN (0)
|
||||
#endif
|
||||
|
||||
// Whether to provide fix for fmod Inf handling.
|
||||
#ifndef MICROPY_PY_MATH_FMOD_FIX_INFNAN
|
||||
#define MICROPY_PY_MATH_FMOD_FIX_INFNAN (0)
|
||||
#endif
|
||||
|
||||
// Whether to provide fix for modf negative zero handling.
|
||||
#ifndef MICROPY_PY_MATH_MODF_FIX_NEGZERO
|
||||
#define MICROPY_PY_MATH_MODF_FIX_NEGZERO (0)
|
||||
#endif
|
||||
|
||||
// Whether to provide "cmath" module
|
||||
#ifndef MICROPY_PY_CMATH
|
||||
#define MICROPY_PY_CMATH (0)
|
||||
|
@ -39,8 +39,8 @@ for name, f, args in (
|
||||
# double argument functions
|
||||
for name, f, args in (
|
||||
("pow", math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3))),
|
||||
("fmod", math.fmod, ((1.2, inf), (1.2, 0), (inf, 1.2))),
|
||||
("atan2", math.atan2, ((0, 0),)),
|
||||
("fmod", math.fmod, ((1.2, inf), (1.2, -inf), (1.2, 0), (inf, 1.2))),
|
||||
("atan2", math.atan2, ((0, 0), (-inf, inf), (-inf, -inf), (inf, -inf))),
|
||||
("copysign", math.copysign, ()),
|
||||
):
|
||||
for x in args + ((0, inf), (inf, 0), (inf, inf), (inf, nan), (nan, inf), (nan, nan)):
|
||||
|
Loading…
x
Reference in New Issue
Block a user