Browse Source
This patch changes how most of the plain math functions are implemented: there are now two generic math wrapper functions that take a pointer to a math function (like sin, cos) and perform the necessary conversion to and from MicroPython types. This helps to reduce code size. The generic functions can also check for math domain errors in a generic way, by testing if the result is NaN or infinity combined with finite inputs. The result is that, with this patch, all math functions now have full domain error checking (even gamma and lgamma) and code size has decreased for most ports. Code size changes in bytes for those with the math module are: unix x64: -432 unix nanbox: -792 stm32: -88 esp8266: +12 Tests are also added to check domain errors are handled correctly.pull/3347/merge
Damien George
7 years ago
3 changed files with 132 additions and 20 deletions
@ -0,0 +1,51 @@ |
|||||
|
# Tests domain errors in math functions |
||||
|
|
||||
|
try: |
||||
|
import math |
||||
|
except ImportError: |
||||
|
print("SKIP") |
||||
|
raise SystemExit |
||||
|
|
||||
|
inf = float('inf') |
||||
|
nan = float('nan') |
||||
|
|
||||
|
# single argument functions |
||||
|
for name, f, args in ( |
||||
|
('fabs', math.fabs, ()), |
||||
|
('ceil', math.ceil, ()), |
||||
|
('floor', math.floor, ()), |
||||
|
('trunc', math.trunc, ()), |
||||
|
('sqrt', math.sqrt, (-1, 0)), |
||||
|
('exp', math.exp, ()), |
||||
|
('sin', math.sin, ()), |
||||
|
('cos', math.cos, ()), |
||||
|
('tan', math.tan, ()), |
||||
|
('asin', math.asin, (-1.1, 1, 1.1)), |
||||
|
('acos', math.acos, (-1.1, 1, 1.1)), |
||||
|
('atan', math.atan, ()), |
||||
|
('ldexp', lambda x: math.ldexp(x, 0), ()), |
||||
|
('radians', math.radians, ()), |
||||
|
('degrees', math.degrees, ()), |
||||
|
): |
||||
|
for x in args + (inf, nan): |
||||
|
try: |
||||
|
ans = f(x) |
||||
|
print('%.4f' % ans) |
||||
|
except ValueError: |
||||
|
print(name, 'ValueError') |
||||
|
except OverflowError: |
||||
|
print(name, 'OverflowError') |
||||
|
|
||||
|
# double argument functions |
||||
|
for name, f, args in ( |
||||
|
('pow', math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3))), |
||||
|
('fmod', math.fmod, ((1.2, inf), (1.2, 0), (inf, 1.2))), |
||||
|
('atan2', math.atan2, ((0, 0),)), |
||||
|
('copysign', math.copysign, ()), |
||||
|
): |
||||
|
for x in args + ((0, inf), (inf, 0), (inf, inf), (inf, nan), (nan, inf), (nan, nan)): |
||||
|
try: |
||||
|
ans = f(*x) |
||||
|
print('%.4f' % ans) |
||||
|
except ValueError: |
||||
|
print(name, 'ValueError') |
@ -0,0 +1,36 @@ |
|||||
|
# Tests domain errors in special math functions |
||||
|
|
||||
|
try: |
||||
|
import math |
||||
|
math.erf |
||||
|
except (ImportError, AttributeError): |
||||
|
print("SKIP") |
||||
|
raise SystemExit |
||||
|
|
||||
|
inf = float('inf') |
||||
|
nan = float('nan') |
||||
|
|
||||
|
# single argument functions |
||||
|
for name, f, args in ( |
||||
|
('expm1', math.exp, ()), |
||||
|
('log2', math.log2, (-1, 0)), |
||||
|
('log10', math.log10, (-1, 0)), |
||||
|
('sinh', math.sinh, ()), |
||||
|
('cosh', math.cosh, ()), |
||||
|
('tanh', math.tanh, ()), |
||||
|
('asinh', math.asinh, ()), |
||||
|
('acosh', math.acosh, (-1, 0.9, 1)), |
||||
|
('atanh', math.atanh, (-1, 1)), |
||||
|
('erf', math.erf, ()), |
||||
|
('erfc', math.erfc, ()), |
||||
|
('gamma', math.gamma, (-2, -1, 0, 1)), |
||||
|
('lgamma', math.lgamma, (-2, -1, 0, 1)), |
||||
|
): |
||||
|
for x in args + (inf, nan): |
||||
|
try: |
||||
|
ans = f(x) |
||||
|
print('%.4f' % ans) |
||||
|
except ValueError: |
||||
|
print(name, 'ValueError') |
||||
|
except OverflowError: |
||||
|
print(name, 'OverflowError') |
Loading…
Reference in new issue