Skip to content

Commit 2e54d9d

Browse files
stinosdpgeorge
authored andcommitted
py: Fix handling of NaN in certain pow implementations.
Adds a new compile-time option MICROPY_PY_MATH_POW_FIX_NAN for use with toolchains that don't handle pow-of-NaN correctly.
1 parent 8d5a40c commit 2e54d9d

File tree

5 files changed

+26
-1
lines changed

5 files changed

+26
-1
lines changed

ports/windows/mpconfigport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ extern const struct _mp_obj_module_t mp_module_time;
236236
#define MICROPY_PY_MATH_FMOD_FIX_INFNAN (1)
237237
#ifdef _WIN64
238238
#define MICROPY_PY_MATH_MODF_FIX_NEGZERO (1)
239+
#else
240+
#define MICROPY_PY_MATH_POW_FIX_NAN (1)
239241
#endif
240242
#endif
241243

py/modmath.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,19 @@ mp_float_t MICROPY_FLOAT_C_FUN(log2)(mp_float_t x) {
9898
// sqrt(x): returns the square root of x
9999
MATH_FUN_1(sqrt, sqrt)
100100
// pow(x, y): returns x to the power of y
101+
#if MICROPY_PY_MATH_POW_FIX_NAN
102+
mp_float_t pow_func(mp_float_t x, mp_float_t y) {
103+
// pow(base, 0) returns 1 for any base, even when base is NaN
104+
// pow(+1, exponent) returns 1 for any exponent, even when exponent is NaN
105+
if (x == MICROPY_FLOAT_CONST(1.0) || y == MICROPY_FLOAT_CONST(0.0)) {
106+
return MICROPY_FLOAT_CONST(1.0);
107+
}
108+
return MICROPY_FLOAT_C_FUN(pow)(x, y);
109+
}
110+
MATH_FUN_2(pow, pow_func)
111+
#else
101112
MATH_FUN_2(pow, pow)
113+
#endif
102114
// exp(x)
103115
MATH_FUN_1(exp, exp)
104116
#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS

py/mpconfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,11 @@ typedef double mp_float_t;
11601160
#define MICROPY_PY_MATH_MODF_FIX_NEGZERO (0)
11611161
#endif
11621162

1163+
// Whether to provide fix for pow(1, NaN) and pow(NaN, 0), which both should be 1 not NaN.
1164+
#ifndef MICROPY_PY_MATH_POW_FIX_NAN
1165+
#define MICROPY_PY_MATH_POW_FIX_NAN (0)
1166+
#endif
1167+
11631168
// Whether to provide "cmath" module
11641169
#ifndef MICROPY_PY_CMATH
11651170
#define MICROPY_PY_CMATH (0)

py/objfloat.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,12 @@ mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t
300300
mp_raise_ValueError(MP_ERROR_TEXT("complex values not supported"));
301301
#endif
302302
}
303+
#if MICROPY_PY_MATH_POW_FIX_NAN // Also see modmath.c.
304+
if (lhs_val == MICROPY_FLOAT_CONST(1.0) || rhs_val == MICROPY_FLOAT_CONST(0.0)) {
305+
lhs_val = MICROPY_FLOAT_CONST(1.0);
306+
break;
307+
}
308+
#endif
303309
lhs_val = MICROPY_FLOAT_C_FUN(pow)(lhs_val, rhs_val);
304310
break;
305311
case MP_BINARY_OP_DIVMOD: {

tests/float/math_domain.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
# double argument functions
4040
for name, f, args in (
41-
("pow", math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3))),
41+
("pow", math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3), (nan, 0), (1, nan))),
4242
("fmod", math.fmod, ((1.2, inf), (1.2, -inf), (1.2, 0), (inf, 1.2))),
4343
("atan2", math.atan2, ((0, 0), (-inf, inf), (-inf, -inf), (inf, -inf))),
4444
("copysign", math.copysign, ()),

0 commit comments

Comments
 (0)