Skip to content

Commit e57aa7e

Browse files
yoctopucedpgeorge
authored andcommitted
py/obj: Fix nan handling in object REPR_C and REPR_D.
CPython math.nan is positive with regards to copysign. The signaling bit (aka sign flag) was incorrectly set. In addition, REPR_C and REPR_D should only use the _true_ nan to prevent system crash in case of hand-crafted floats. For instance, with REPR_C, any nan-like float following the pattern `01111111 1xxxxxxx xxxxxxxx xxxxx1xx` would be switched to an immediate object or a qstr string. When the qstr index is too large, this would cause a crash. This commit fixes the issue, and adds the relevant test cases. Signed-off-by: Yoctopuce dev <[email protected]>
1 parent 66c0148 commit e57aa7e

File tree

5 files changed

+37
-4
lines changed

5 files changed

+37
-4
lines changed

ports/windows/mpconfigport.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,11 @@ typedef long mp_off_t;
266266
#endif
267267
#endif
268268

269+
// VC++ 2017 fixes
270+
#if (_MSC_VER < 1920)
271+
#define MICROPY_PY_MATH_COPYSIGN_FIX_NAN (1)
272+
#endif
273+
269274
// CL specific definitions
270275

271276
#ifndef __cplusplus

py/modmath.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ MATH_FUN_2(atan2, atan2)
161161
MATH_FUN_1_TO_INT(ceil, ceil)
162162
// copysign(x, y)
163163
static mp_float_t MICROPY_FLOAT_C_FUN(copysign_func)(mp_float_t x, mp_float_t y) {
164+
#if MICROPY_PY_MATH_COPYSIGN_FIX_NAN
165+
if (isnan(y)) {
166+
y = 0.0;
167+
}
168+
#endif
164169
return MICROPY_FLOAT_C_FUN(copysign)(x, y);
165170
}
166171
MATH_FUN_2(copysign, copysign_func)

py/obj.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,15 @@ static inline bool mp_obj_is_small_int(mp_const_obj_t o) {
184184
#define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 1) | 1))
185185

186186
#if MICROPY_PY_BUILTINS_FLOAT
187-
#define MP_OBJ_NEW_CONST_FLOAT(f) MP_ROM_PTR((mp_obj_t)((((((uint64_t)f) & ~3) | 2) + 0x80800000) & 0xffffffff))
187+
#include <math.h>
188+
// note: MP_OBJ_NEW_CONST_FLOAT should be a MP_ROM_PTR but that macro isn't available yet
189+
#define MP_OBJ_NEW_CONST_FLOAT(f) ((mp_obj_t)((((((uint64_t)f) & ~3) | 2) + 0x80800000) & 0xffffffff))
188190
#define mp_const_float_e MP_OBJ_NEW_CONST_FLOAT(0x402df854)
189191
#define mp_const_float_pi MP_OBJ_NEW_CONST_FLOAT(0x40490fdb)
192+
#define mp_const_float_nan MP_OBJ_NEW_CONST_FLOAT(0x7fc00000)
190193
#if MICROPY_PY_MATH_CONSTANTS
191194
#define mp_const_float_tau MP_OBJ_NEW_CONST_FLOAT(0x40c90fdb)
192195
#define mp_const_float_inf MP_OBJ_NEW_CONST_FLOAT(0x7f800000)
193-
#define mp_const_float_nan MP_OBJ_NEW_CONST_FLOAT(0xffc00000)
194196
#endif
195197

196198
static inline bool mp_obj_is_float(mp_const_obj_t o) {
@@ -207,6 +209,10 @@ static inline mp_float_t mp_obj_float_get(mp_const_obj_t o) {
207209
return num.f;
208210
}
209211
static inline mp_obj_t mp_obj_new_float(mp_float_t f) {
212+
if (isnan(f)) {
213+
// prevent creation of bad nanboxed pointers via array.array or struct
214+
return mp_const_float_nan;
215+
}
210216
union {
211217
mp_float_t f;
212218
mp_uint_t u;
@@ -257,12 +263,13 @@ static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) {
257263
#error MICROPY_OBJ_REPR_D requires MICROPY_FLOAT_IMPL_DOUBLE
258264
#endif
259265

266+
#include <math.h>
260267
#define mp_const_float_e {((mp_obj_t)((uint64_t)0x4005bf0a8b145769 + 0x8004000000000000))}
261268
#define mp_const_float_pi {((mp_obj_t)((uint64_t)0x400921fb54442d18 + 0x8004000000000000))}
269+
#define mp_const_float_nan {((mp_obj_t)((uint64_t)0x7ff8000000000000 + 0x8004000000000000))}
262270
#if MICROPY_PY_MATH_CONSTANTS
263271
#define mp_const_float_tau {((mp_obj_t)((uint64_t)0x401921fb54442d18 + 0x8004000000000000))}
264272
#define mp_const_float_inf {((mp_obj_t)((uint64_t)0x7ff0000000000000 + 0x8004000000000000))}
265-
#define mp_const_float_nan {((mp_obj_t)((uint64_t)0xfff8000000000000 + 0x8004000000000000))}
266273
#endif
267274

268275
static inline bool mp_obj_is_float(mp_const_obj_t o) {
@@ -276,6 +283,13 @@ static inline mp_float_t mp_obj_float_get(mp_const_obj_t o) {
276283
return num.f;
277284
}
278285
static inline mp_obj_t mp_obj_new_float(mp_float_t f) {
286+
if (isnan(f)) {
287+
// prevent creation of bad nanboxed pointers via array.array or struct
288+
struct {
289+
uint64_t r;
290+
} num = mp_const_float_nan;
291+
return num.r;
292+
}
279293
union {
280294
mp_float_t f;
281295
uint64_t r;

tests/float/float_array.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,10 @@ def test(a):
1919
test(array("f"))
2020
test(array("d"))
2121

22-
print("{:.4f}".format(array("f", bytes(array("I", [0x3DCCCCCC])))[0]))
22+
# hand-crafted floats, including non-standard nan
23+
for float_hex in (0x3DCCCCCC, 0x7F800024, 0x7FC00004):
24+
f = array("f", bytes(array("I", [float_hex])))[0]
25+
if type(f) is float:
26+
print("{:.4e}".format(f))
27+
else:
28+
print(f)

tests/float/math_constants_extra.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
raise SystemExit
1010

1111
print(math.tau == 2.0 * math.pi)
12+
print(math.copysign(1.0, math.tau))
1213

1314
print(math.inf == float("inf"))
1415
print(-math.inf == -float("inf"))
16+
print(math.copysign(1.0, math.inf))
1517

1618
print(isnan(math.nan))
1719
print(isnan(-math.nan))
20+
print(math.copysign(1.0, math.nan))

0 commit comments

Comments
 (0)