From 0ba68f8a1dae84de950420aebf8ad46582a38e66 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 9 Apr 2020 09:05:48 +0200 Subject: [PATCH] all: Fix implicit floating point promotion. Initially some of these were found building the unix coverage variant on MacOS because that build uses clang and has -Wdouble-promotion enabled, and clang performs more vigorous promotion checks than gcc. Additionally the codebase has been compiled with clang and msvc (the latter with warning level 3), and with MICROPY_FLOAT_IMPL_FLOAT to find the rest of the conversions. Fixes are implemented either as explicit casts, or by using the correct type, or by using one of the utility functions to handle floating point casting; these have been moved from nativeglue.c to the public API. --- extmod/moductypes.c | 7 +++---- extmod/moduselect.c | 2 +- ports/unix/modffi.c | 4 ++-- ports/unix/modtime.c | 8 ++++---- ports/unix/modusocket.c | 4 ++-- py/binary.c | 8 ++++---- py/modcmath.c | 8 ++++---- py/nativeglue.c | 20 +------------------- py/obj.h | 33 +++++++++++++++++++++++++++++++++ py/objfloat.c | 28 +++++++++++++++------------- py/parsenum.c | 2 +- 11 files changed, 70 insertions(+), 54 deletions(-) diff --git a/extmod/moductypes.c b/extmod/moductypes.c index eb9ca57e3..cdbf49ad4 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -360,7 +360,7 @@ STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) { return mp_obj_new_int_from_ll(((int64_t *)p)[index]); #if MICROPY_PY_BUILTINS_FLOAT case FLOAT32: - return mp_obj_new_float(((float *)p)[index]); + return mp_obj_new_float_from_f(((float *)p)[index]); case FLOAT64: return mp_obj_new_float(((double *)p)[index]); #endif @@ -373,11 +373,10 @@ STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) { STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) { #if MICROPY_PY_BUILTINS_FLOAT if (val_type == FLOAT32 || val_type == FLOAT64) { - mp_float_t v = mp_obj_get_float(val); if (val_type == FLOAT32) { - ((float *)p)[index] = v; + ((float *)p)[index] = mp_obj_get_float_to_f(val); } else { - ((double *)p)[index] = v; + ((double *)p)[index] = mp_obj_get_float_to_d(val); } return; } diff --git a/extmod/moduselect.c b/extmod/moduselect.c index 122365607..80beb8e09 100644 --- a/extmod/moduselect.c +++ b/extmod/moduselect.c @@ -125,7 +125,7 @@ STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) { if (n_args == 4) { if (args[3] != mp_const_none) { #if MICROPY_PY_BUILTINS_FLOAT - float timeout_f = mp_obj_get_float(args[3]); + float timeout_f = mp_obj_get_float_to_f(args[3]); if (timeout_f >= 0) { timeout = (mp_uint_t)(timeout_f * 1000); } diff --git a/ports/unix/modffi.c b/ports/unix/modffi.c index 3a9a7eca6..e5bd092cc 100644 --- a/ports/unix/modffi.c +++ b/ports/unix/modffi.c @@ -167,7 +167,7 @@ STATIC mp_obj_t return_ffi_value(ffi_arg val, char type) { union { ffi_arg ffi; float flt; } val_union = { .ffi = val }; - return mp_obj_new_float(val_union.flt); + return mp_obj_new_float_from_f(val_union.flt); } case 'd': { double *p = (double *)&val; @@ -381,7 +381,7 @@ STATIC mp_obj_t ffifunc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const *p = mp_obj_get_float(a); } else if (*argtype == 'd') { double *p = (double *)&values[i]; - *p = mp_obj_get_float(a); + *p = mp_obj_get_float_to_d(a); #endif } else if (a == mp_const_none) { values[i] = 0; diff --git a/ports/unix/modtime.c b/ports/unix/modtime.c index 126aec30c..16152152a 100644 --- a/ports/unix/modtime.c +++ b/ports/unix/modtime.c @@ -61,7 +61,7 @@ static inline int msec_sleep_tv(struct timeval *tv) { #endif #if defined(MP_CLOCKS_PER_SEC) -#define CLOCK_DIV (MP_CLOCKS_PER_SEC / 1000.0F) +#define CLOCK_DIV (MP_CLOCKS_PER_SEC / MICROPY_FLOAT_CONST(1000.0)) #else #error Unsupported clock() implementation #endif @@ -84,7 +84,7 @@ STATIC mp_obj_t mod_time_clock(void) { // float cannot represent full range of int32 precisely, so we pre-divide // int to reduce resolution, and then actually do float division hoping // to preserve integer part resolution. - return mp_obj_new_float((float)(clock() / 1000) / CLOCK_DIV); + return mp_obj_new_float((clock() / 1000) / CLOCK_DIV); #else return mp_obj_new_int((mp_int_t)clock()); #endif @@ -95,8 +95,8 @@ STATIC mp_obj_t mod_time_sleep(mp_obj_t arg) { #if MICROPY_PY_BUILTINS_FLOAT struct timeval tv; mp_float_t val = mp_obj_get_float(arg); - double ipart; - tv.tv_usec = round(modf(val, &ipart) * 1000000); + mp_float_t ipart; + tv.tv_usec = MICROPY_FLOAT_C_FUN(round)(MICROPY_FLOAT_C_FUN(modf)(val, &ipart) * MICROPY_FLOAT_CONST(1000000.)); tv.tv_sec = ipart; int res; while (1) { diff --git a/ports/unix/modusocket.c b/ports/unix/modusocket.c index 8bc453d42..f87a717c9 100644 --- a/ports/unix/modusocket.c +++ b/ports/unix/modusocket.c @@ -378,8 +378,8 @@ STATIC mp_obj_t socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in) { if (timeout_in != mp_const_none) { #if MICROPY_PY_BUILTINS_FLOAT mp_float_t val = mp_obj_get_float(timeout_in); - double ipart; - tv.tv_usec = round(modf(val, &ipart) * 1000000); + mp_float_t ipart; + tv.tv_usec = MICROPY_FLOAT_C_FUN(round)(MICROPY_FLOAT_C_FUN(modf)(val, &ipart) * MICROPY_FLOAT_CONST(1000000.)); tv.tv_sec = ipart; #else tv.tv_sec = mp_obj_get_int(timeout_in); diff --git a/py/binary.c b/py/binary.c index b29438770..33515e880 100644 --- a/py/binary.c +++ b/py/binary.c @@ -176,7 +176,7 @@ mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index) { #endif #if MICROPY_PY_BUILTINS_FLOAT case 'f': - return mp_obj_new_float(((float *)p)[index]); + return mp_obj_new_float_from_f(((float *)p)[index]); case 'd': return mp_obj_new_float(((double *)p)[index]); #endif @@ -244,7 +244,7 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * union { uint32_t i; float f; } fpu = {val}; - return mp_obj_new_float(fpu.f); + return mp_obj_new_float_from_f(fpu.f); } else if (val_type == 'd') { union { uint64_t i; double f; @@ -320,7 +320,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p uint32_t i32[2]; double f; } fp_dp; - fp_dp.f = mp_obj_get_float(val_in); + fp_dp.f = mp_obj_get_float_to_d(val_in); if (BYTES_PER_WORD == 8) { val = fp_dp.i64; } else { @@ -362,7 +362,7 @@ void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_ ((float *)p)[index] = mp_obj_get_float(val_in); break; case 'd': - ((double *)p)[index] = mp_obj_get_float(val_in); + ((double *)p)[index] = mp_obj_get_float_to_d(val_in); break; #endif // Extension to CPython: array of objects diff --git a/py/modcmath.c b/py/modcmath.c index ce1f4746f..f1ce8fb5d 100644 --- a/py/modcmath.c +++ b/py/modcmath.c @@ -72,7 +72,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_exp_obj, mp_cmath_exp); STATIC mp_obj_t mp_cmath_log(mp_obj_t z_obj) { mp_float_t real, imag; mp_obj_get_complex(z_obj, &real, &imag); - return mp_obj_new_complex(0.5 * MICROPY_FLOAT_C_FUN(log)(real * real + imag * imag), MICROPY_FLOAT_C_FUN(atan2)(imag, real)); + return mp_obj_new_complex(MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(log)(real * real + imag * imag), MICROPY_FLOAT_C_FUN(atan2)(imag, real)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_log_obj, mp_cmath_log); @@ -81,7 +81,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_log_obj, mp_cmath_log); STATIC mp_obj_t mp_cmath_log10(mp_obj_t z_obj) { mp_float_t real, imag; mp_obj_get_complex(z_obj, &real, &imag); - return mp_obj_new_complex(0.5 * MICROPY_FLOAT_C_FUN(log10)(real * real + imag * imag), 0.4342944819032518 * MICROPY_FLOAT_C_FUN(atan2)(imag, real)); + return mp_obj_new_complex(MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(log10)(real * real + imag * imag), MICROPY_FLOAT_CONST(0.4342944819032518) * MICROPY_FLOAT_C_FUN(atan2)(imag, real)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_log10_obj, mp_cmath_log10); #endif @@ -90,8 +90,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_log10_obj, mp_cmath_log10); STATIC mp_obj_t mp_cmath_sqrt(mp_obj_t z_obj) { mp_float_t real, imag; mp_obj_get_complex(z_obj, &real, &imag); - mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(pow)(real * real + imag * imag, 0.25); - mp_float_t theta = 0.5 * MICROPY_FLOAT_C_FUN(atan2)(imag, real); + mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(pow)(real * real + imag * imag, MICROPY_FLOAT_CONST(0.25)); + mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real); return mp_obj_new_complex(sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta), sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_sqrt_obj, mp_cmath_sqrt); diff --git a/py/nativeglue.c b/py/nativeglue.c index eebbe18bb..30e5b4006 100644 --- a/py/nativeglue.c +++ b/py/nativeglue.c @@ -227,25 +227,7 @@ STATIC bool mp_native_yield_from(mp_obj_t gen, mp_obj_t send_value, mp_obj_t *re return false; } -#if MICROPY_PY_BUILTINS_FLOAT - -STATIC mp_obj_t mp_obj_new_float_from_f(float f) { - return mp_obj_new_float((mp_float_t)f); -} - -STATIC mp_obj_t mp_obj_new_float_from_d(double d) { - return mp_obj_new_float((mp_float_t)d); -} - -STATIC float mp_obj_get_float_to_f(mp_obj_t o) { - return (float)mp_obj_get_float(o); -} - -STATIC double mp_obj_get_float_to_d(mp_obj_t o) { - return (double)mp_obj_get_float(o); -} - -#else +#if !MICROPY_PY_BUILTINS_FLOAT STATIC mp_obj_t mp_obj_new_float_from_f(float f) { (void)f; diff --git a/py/obj.h b/py/obj.h index b282c63db..d3abdab59 100644 --- a/py/obj.h +++ b/py/obj.h @@ -820,6 +820,39 @@ void mp_str_print_quoted(const mp_print_t *print, const byte *str_data, size_t s #if MICROPY_PY_BUILTINS_FLOAT // float +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +static inline float mp_obj_get_float_to_f(mp_obj_t o) { + return mp_obj_get_float(o); +} + +static inline double mp_obj_get_float_to_d(mp_obj_t o) { + return (double)mp_obj_get_float(o); +} + +static inline mp_obj_t mp_obj_new_float_from_f(float o) { + return mp_obj_new_float(o); +} + +static inline mp_obj_t mp_obj_new_float_from_d(double o) { + return mp_obj_new_float((mp_float_t)o); +} +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +static inline float mp_obj_get_float_to_f(mp_obj_t o) { + return (float)mp_obj_get_float(o); +} + +static inline double mp_obj_get_float_to_d(mp_obj_t o) { + return mp_obj_get_float(o); +} + +static inline mp_obj_t mp_obj_new_float_from_f(float o) { + return mp_obj_new_float((mp_float_t)o); +} + +static inline mp_obj_t mp_obj_new_float_from_d(double o) { + return mp_obj_new_float(o); +} +#endif #if MICROPY_FLOAT_HIGH_QUALITY_HASH mp_int_t mp_float_hash(mp_float_t val); #else diff --git a/py/objfloat.c b/py/objfloat.c index c7cf049fb..09b73c8cd 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -52,11 +52,13 @@ typedef struct _mp_obj_float_t { mp_float_t value; } mp_obj_float_t; -const mp_obj_float_t mp_const_float_e_obj = {{&mp_type_float}, M_E}; -const mp_obj_float_t mp_const_float_pi_obj = {{&mp_type_float}, M_PI}; +const mp_obj_float_t mp_const_float_e_obj = {{&mp_type_float}, (mp_float_t)M_E}; +const mp_obj_float_t mp_const_float_pi_obj = {{&mp_type_float}, (mp_float_t)M_PI}; #endif +#define MICROPY_FLOAT_ZERO MICROPY_FLOAT_CONST(0.0) + #if MICROPY_FLOAT_HIGH_QUALITY_HASH // must return actual integer value if it fits in mp_int_t mp_int_t mp_float_hash(mp_float_t src) { @@ -208,24 +210,24 @@ STATIC void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y) { mp_float_t div = (*x - mod) / *y; // Python specs require that mod has same sign as second operand - if (mod == 0.0) { - mod = MICROPY_FLOAT_C_FUN(copysign)(0.0, *y); + if (mod == MICROPY_FLOAT_ZERO) { + mod = MICROPY_FLOAT_C_FUN(copysign)(MICROPY_FLOAT_ZERO, *y); } else { - if ((mod < 0.0) != (*y < 0.0)) { + if ((mod < MICROPY_FLOAT_ZERO) != (*y < MICROPY_FLOAT_ZERO)) { mod += *y; - div -= 1.0; + div -= MICROPY_FLOAT_CONST(1.0); } } mp_float_t floordiv; - if (div == 0.0) { + if (div == MICROPY_FLOAT_ZERO) { // if division is zero, take the correct sign of zero - floordiv = MICROPY_FLOAT_C_FUN(copysign)(0.0, *x / *y); + floordiv = MICROPY_FLOAT_C_FUN(copysign)(MICROPY_FLOAT_ZERO, *x / *y); } else { // Python specs require that x == (x//y)*y + (x%y) floordiv = MICROPY_FLOAT_C_FUN(floor)(div); - if (div - floordiv > 0.5) { - floordiv += 1.0; + if (div - floordiv > MICROPY_FLOAT_CONST(0.5)) { + floordiv += MICROPY_FLOAT_CONST(1.0); } } @@ -273,15 +275,15 @@ mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t break; case MP_BINARY_OP_MODULO: case MP_BINARY_OP_INPLACE_MODULO: - if (rhs_val == 0) { + if (rhs_val == MICROPY_FLOAT_ZERO) { goto zero_division_error; } lhs_val = MICROPY_FLOAT_C_FUN(fmod)(lhs_val, rhs_val); // Python specs require that mod has same sign as second operand - if (lhs_val == 0.0) { + if (lhs_val == MICROPY_FLOAT_ZERO) { lhs_val = MICROPY_FLOAT_C_FUN(copysign)(0.0, rhs_val); } else { - if ((lhs_val < 0.0) != (rhs_val < 0.0)) { + if ((lhs_val < MICROPY_FLOAT_ZERO) != (rhs_val < MICROPY_FLOAT_ZERO)) { lhs_val += rhs_val; } } diff --git a/py/parsenum.c b/py/parsenum.c index 234248c9d..e665da7d8 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -220,7 +220,7 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool if (str + 2 < top && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'f') { // inf str += 3; - dec_val = INFINITY; + dec_val = (mp_float_t)INFINITY; if (str + 4 < top && (str[0] | 0x20) == 'i' && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'i' && (str[3] | 0x20) == 't' && (str[4] | 0x20) == 'y') { // infinity str += 5;