py/modmath: Implement math.isclose() for non-complex numbers.
As per PEP 485, this function appeared in for Python 3.5. Configured via MICROPY_PY_MATH_ISCLOSE which is disabled by default, but enabled for the ports which already have MICROPY_PY_MATH_SPECIAL_FUNCTIONS enabled.
This commit is contained in:
parent
3eff81288c
commit
af5c998f37
|
@ -98,6 +98,7 @@
|
|||
#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1)
|
||||
#define MICROPY_PY_MATH (1)
|
||||
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
|
||||
#define MICROPY_PY_MATH_ISCLOSE (1)
|
||||
#define MICROPY_PY_CMATH (1)
|
||||
#define MICROPY_PY_GC (1)
|
||||
#define MICROPY_PY_IO (1)
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
#define MICROPY_PY_COLLECTIONS (1)
|
||||
#define MICROPY_PY_MATH (1)
|
||||
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
|
||||
#define MICROPY_PY_MATH_ISCLOSE (1)
|
||||
#define MICROPY_PY_CMATH (1)
|
||||
#define MICROPY_PY_IO (1)
|
||||
#define MICROPY_PY_STRUCT (1)
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
#define MICROPY_PY_COLLECTIONS_DEQUE (1)
|
||||
#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1)
|
||||
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
|
||||
#define MICROPY_PY_MATH_ISCLOSE (1)
|
||||
#define MICROPY_PY_MATH_FACTORIAL (1)
|
||||
#define MICROPY_PY_CMATH (1)
|
||||
#define MICROPY_PY_IO (1)
|
||||
|
|
|
@ -104,6 +104,7 @@
|
|||
#ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS
|
||||
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
|
||||
#endif
|
||||
#define MICROPY_PY_MATH_ISCLOSE (MICROPY_PY_MATH_SPECIAL_FUNCTIONS)
|
||||
#define MICROPY_PY_CMATH (1)
|
||||
#define MICROPY_PY_IO_IOBASE (1)
|
||||
#define MICROPY_PY_IO_FILEIO (1)
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
#define MICROPY_PY_COLLECTIONS_DEQUE (1)
|
||||
#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1)
|
||||
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
|
||||
#define MICROPY_PY_MATH_ISCLOSE (1)
|
||||
#define MICROPY_PY_CMATH (1)
|
||||
#define MICROPY_PY_IO_FILEIO (1)
|
||||
#define MICROPY_PY_GC_COLLECT_RETVAL (1)
|
||||
|
|
39
py/modmath.c
39
py/modmath.c
|
@ -171,6 +171,42 @@ MATH_FUN_1(lgamma, lgamma)
|
|||
#endif
|
||||
//TODO: fsum
|
||||
|
||||
#if MICROPY_PY_MATH_ISCLOSE
|
||||
STATIC mp_obj_t mp_math_isclose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_a, ARG_b, ARG_rel_tol, ARG_abs_tol };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ},
|
||||
{MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ},
|
||||
{MP_QSTR_rel_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
|
||||
{MP_QSTR_abs_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(0)}},
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
const mp_float_t a = mp_obj_get_float(args[ARG_a].u_obj);
|
||||
const mp_float_t b = mp_obj_get_float(args[ARG_b].u_obj);
|
||||
const mp_float_t rel_tol = args[ARG_rel_tol].u_obj == MP_OBJ_NULL
|
||||
? (mp_float_t)1e-9 : mp_obj_get_float(args[ARG_rel_tol].u_obj);
|
||||
const mp_float_t abs_tol = mp_obj_get_float(args[ARG_abs_tol].u_obj);
|
||||
if (rel_tol < (mp_float_t)0.0 || abs_tol < (mp_float_t)0.0) {
|
||||
math_error();
|
||||
}
|
||||
if (a == b) {
|
||||
return mp_const_true;
|
||||
}
|
||||
const mp_float_t difference = MICROPY_FLOAT_C_FUN(fabs)(a - b);
|
||||
if (isinf(difference)) { // Either a or b is inf
|
||||
return mp_const_false;
|
||||
}
|
||||
if ((difference <= abs_tol) ||
|
||||
(difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * a)) ||
|
||||
(difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * b))) {
|
||||
return mp_const_true;
|
||||
}
|
||||
return mp_const_false;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(mp_math_isclose_obj, 2, mp_math_isclose);
|
||||
#endif
|
||||
|
||||
// Function that takes a variable number of arguments
|
||||
|
||||
// log(x[, base])
|
||||
|
@ -335,6 +371,9 @@ STATIC const mp_rom_map_elem_t mp_module_math_globals_table[] = {
|
|||
{ MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&mp_math_isfinite_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&mp_math_isinf_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_isnan), MP_ROM_PTR(&mp_math_isnan_obj) },
|
||||
#if MICROPY_PY_MATH_ISCLOSE
|
||||
{ MP_ROM_QSTR(MP_QSTR_isclose), MP_ROM_PTR(&mp_math_isclose_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_trunc), MP_ROM_PTR(&mp_math_trunc_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&mp_math_radians_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&mp_math_degrees_obj) },
|
||||
|
|
|
@ -1079,6 +1079,11 @@ typedef double mp_float_t;
|
|||
#define MICROPY_PY_MATH_FACTORIAL (0)
|
||||
#endif
|
||||
|
||||
// Whether to provide math.isclose function
|
||||
#ifndef MICROPY_PY_MATH_ISCLOSE
|
||||
#define MICROPY_PY_MATH_ISCLOSE (0)
|
||||
#endif
|
||||
|
||||
// Whether to provide "cmath" module
|
||||
#ifndef MICROPY_PY_CMATH
|
||||
#define MICROPY_PY_CMATH (0)
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
# test math.isclose (appeared in Python 3.5)
|
||||
|
||||
try:
|
||||
from math import isclose
|
||||
except ImportError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
def test(a, b, **kwargs):
|
||||
print(isclose(a, b, **kwargs))
|
||||
|
||||
def test_combinations(a, b, **kwargs):
|
||||
test(a, a, **kwargs)
|
||||
test(a, b, **kwargs)
|
||||
test(b, a, **kwargs)
|
||||
test(b, b, **kwargs)
|
||||
|
||||
# Special numbers
|
||||
test_combinations(float('nan'), 1)
|
||||
test_combinations(float('inf'), 1)
|
||||
test_combinations(float('-inf'), 1)
|
||||
|
||||
# Equality
|
||||
test(1.0, 1.0, rel_tol=0.0, abs_tol=0.0)
|
||||
test(2.35e-100, 2.35e-100, rel_tol=0.0, abs_tol=0.0)
|
||||
test(2.1234e100, 2.1234e100, rel_tol=0.0, abs_tol=0.0)
|
||||
|
||||
# Relative tolerance
|
||||
test(1000.0, 1001.0, rel_tol=1e-3)
|
||||
test(1000.0, 1001.0, rel_tol=1e-4)
|
||||
test(1000, 1001, rel_tol=1e-3)
|
||||
test(1000, 1001, rel_tol=1e-4)
|
||||
test_combinations(0, 1, rel_tol=1.0)
|
||||
|
||||
# Absolute tolerance
|
||||
test(0.0, 1e-10, abs_tol=1e-10, rel_tol=0.1)
|
||||
test(0.0, 1e-10, abs_tol=0.0, rel_tol=0.1)
|
||||
|
||||
# Bad parameters
|
||||
try:
|
||||
isclose(0, 0, abs_tol=-1)
|
||||
except ValueError:
|
||||
print('ValueError')
|
||||
try:
|
||||
isclose(0, 0, rel_tol=-1)
|
||||
except ValueError:
|
||||
print('ValueError')
|
|
@ -0,0 +1,27 @@
|
|||
False
|
||||
False
|
||||
False
|
||||
True
|
||||
True
|
||||
False
|
||||
False
|
||||
True
|
||||
True
|
||||
False
|
||||
False
|
||||
True
|
||||
True
|
||||
True
|
||||
True
|
||||
True
|
||||
False
|
||||
True
|
||||
False
|
||||
True
|
||||
True
|
||||
True
|
||||
True
|
||||
True
|
||||
False
|
||||
ValueError
|
||||
ValueError
|
Loading…
Reference in New Issue