Skip to content

Commit af5c998

Browse files
stinosdpgeorge
authored andcommitted
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.
1 parent 3eff812 commit af5c998

File tree

9 files changed

+123
-0
lines changed

9 files changed

+123
-0
lines changed

ports/esp32/mpconfigport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1)
9999
#define MICROPY_PY_MATH (1)
100100
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
101+
#define MICROPY_PY_MATH_ISCLOSE (1)
101102
#define MICROPY_PY_CMATH (1)
102103
#define MICROPY_PY_GC (1)
103104
#define MICROPY_PY_IO (1)

ports/javascript/mpconfigport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
#define MICROPY_PY_COLLECTIONS (1)
7474
#define MICROPY_PY_MATH (1)
7575
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
76+
#define MICROPY_PY_MATH_ISCLOSE (1)
7677
#define MICROPY_PY_CMATH (1)
7778
#define MICROPY_PY_IO (1)
7879
#define MICROPY_PY_STRUCT (1)

ports/stm32/mpconfigport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
#define MICROPY_PY_COLLECTIONS_DEQUE (1)
115115
#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1)
116116
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
117+
#define MICROPY_PY_MATH_ISCLOSE (1)
117118
#define MICROPY_PY_MATH_FACTORIAL (1)
118119
#define MICROPY_PY_CMATH (1)
119120
#define MICROPY_PY_IO (1)

ports/unix/mpconfigport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
#ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS
105105
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
106106
#endif
107+
#define MICROPY_PY_MATH_ISCLOSE (MICROPY_PY_MATH_SPECIAL_FUNCTIONS)
107108
#define MICROPY_PY_CMATH (1)
108109
#define MICROPY_PY_IO_IOBASE (1)
109110
#define MICROPY_PY_IO_FILEIO (1)

ports/windows/mpconfigport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
#define MICROPY_PY_COLLECTIONS_DEQUE (1)
8787
#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1)
8888
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
89+
#define MICROPY_PY_MATH_ISCLOSE (1)
8990
#define MICROPY_PY_CMATH (1)
9091
#define MICROPY_PY_IO_FILEIO (1)
9192
#define MICROPY_PY_GC_COLLECT_RETVAL (1)

py/modmath.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,42 @@ MATH_FUN_1(lgamma, lgamma)
171171
#endif
172172
//TODO: fsum
173173

174+
#if MICROPY_PY_MATH_ISCLOSE
175+
STATIC mp_obj_t mp_math_isclose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
176+
enum { ARG_a, ARG_b, ARG_rel_tol, ARG_abs_tol };
177+
static const mp_arg_t allowed_args[] = {
178+
{MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ},
179+
{MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ},
180+
{MP_QSTR_rel_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
181+
{MP_QSTR_abs_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(0)}},
182+
};
183+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
184+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
185+
const mp_float_t a = mp_obj_get_float(args[ARG_a].u_obj);
186+
const mp_float_t b = mp_obj_get_float(args[ARG_b].u_obj);
187+
const mp_float_t rel_tol = args[ARG_rel_tol].u_obj == MP_OBJ_NULL
188+
? (mp_float_t)1e-9 : mp_obj_get_float(args[ARG_rel_tol].u_obj);
189+
const mp_float_t abs_tol = mp_obj_get_float(args[ARG_abs_tol].u_obj);
190+
if (rel_tol < (mp_float_t)0.0 || abs_tol < (mp_float_t)0.0) {
191+
math_error();
192+
}
193+
if (a == b) {
194+
return mp_const_true;
195+
}
196+
const mp_float_t difference = MICROPY_FLOAT_C_FUN(fabs)(a - b);
197+
if (isinf(difference)) { // Either a or b is inf
198+
return mp_const_false;
199+
}
200+
if ((difference <= abs_tol) ||
201+
(difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * a)) ||
202+
(difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * b))) {
203+
return mp_const_true;
204+
}
205+
return mp_const_false;
206+
}
207+
MP_DEFINE_CONST_FUN_OBJ_KW(mp_math_isclose_obj, 2, mp_math_isclose);
208+
#endif
209+
174210
// Function that takes a variable number of arguments
175211

176212
// log(x[, base])
@@ -335,6 +371,9 @@ STATIC const mp_rom_map_elem_t mp_module_math_globals_table[] = {
335371
{ MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&mp_math_isfinite_obj) },
336372
{ MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&mp_math_isinf_obj) },
337373
{ MP_ROM_QSTR(MP_QSTR_isnan), MP_ROM_PTR(&mp_math_isnan_obj) },
374+
#if MICROPY_PY_MATH_ISCLOSE
375+
{ MP_ROM_QSTR(MP_QSTR_isclose), MP_ROM_PTR(&mp_math_isclose_obj) },
376+
#endif
338377
{ MP_ROM_QSTR(MP_QSTR_trunc), MP_ROM_PTR(&mp_math_trunc_obj) },
339378
{ MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&mp_math_radians_obj) },
340379
{ MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&mp_math_degrees_obj) },

py/mpconfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,11 @@ typedef double mp_float_t;
10791079
#define MICROPY_PY_MATH_FACTORIAL (0)
10801080
#endif
10811081

1082+
// Whether to provide math.isclose function
1083+
#ifndef MICROPY_PY_MATH_ISCLOSE
1084+
#define MICROPY_PY_MATH_ISCLOSE (0)
1085+
#endif
1086+
10821087
// Whether to provide "cmath" module
10831088
#ifndef MICROPY_PY_CMATH
10841089
#define MICROPY_PY_CMATH (0)

tests/float/math_isclose.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# test math.isclose (appeared in Python 3.5)
2+
3+
try:
4+
from math import isclose
5+
except ImportError:
6+
print("SKIP")
7+
raise SystemExit
8+
9+
def test(a, b, **kwargs):
10+
print(isclose(a, b, **kwargs))
11+
12+
def test_combinations(a, b, **kwargs):
13+
test(a, a, **kwargs)
14+
test(a, b, **kwargs)
15+
test(b, a, **kwargs)
16+
test(b, b, **kwargs)
17+
18+
# Special numbers
19+
test_combinations(float('nan'), 1)
20+
test_combinations(float('inf'), 1)
21+
test_combinations(float('-inf'), 1)
22+
23+
# Equality
24+
test(1.0, 1.0, rel_tol=0.0, abs_tol=0.0)
25+
test(2.35e-100, 2.35e-100, rel_tol=0.0, abs_tol=0.0)
26+
test(2.1234e100, 2.1234e100, rel_tol=0.0, abs_tol=0.0)
27+
28+
# Relative tolerance
29+
test(1000.0, 1001.0, rel_tol=1e-3)
30+
test(1000.0, 1001.0, rel_tol=1e-4)
31+
test(1000, 1001, rel_tol=1e-3)
32+
test(1000, 1001, rel_tol=1e-4)
33+
test_combinations(0, 1, rel_tol=1.0)
34+
35+
# Absolute tolerance
36+
test(0.0, 1e-10, abs_tol=1e-10, rel_tol=0.1)
37+
test(0.0, 1e-10, abs_tol=0.0, rel_tol=0.1)
38+
39+
# Bad parameters
40+
try:
41+
isclose(0, 0, abs_tol=-1)
42+
except ValueError:
43+
print('ValueError')
44+
try:
45+
isclose(0, 0, rel_tol=-1)
46+
except ValueError:
47+
print('ValueError')

tests/float/math_isclose.py.exp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
False
2+
False
3+
False
4+
True
5+
True
6+
False
7+
False
8+
True
9+
True
10+
False
11+
False
12+
True
13+
True
14+
True
15+
True
16+
True
17+
False
18+
True
19+
False
20+
True
21+
True
22+
True
23+
True
24+
True
25+
False
26+
ValueError
27+
ValueError

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy