Following discussions in PR #16666, this commit updates the float formatting code to improve the `repr` reversibility, i.e. the percentage of valid floating point numbers that do parse back to the same number when formatted by `repr` (in CPython it's 100%). This new code offers a choice of 3 float conversion methods, depending on the desired tradeoff between code size and conversion precision: - BASIC method is the smallest code footprint - APPROX method uses an iterative method to approximate the exact representation, which is a bit slower but but does not have a big impact on code size. It provides `repr` reversibility on >99.8% of the cases in double precision, and on >98.5% in single precision (except with REPR_C, where reversibility is 100% as the last two bits are not taken into account). - EXACT method uses higher-precision floats during conversion, which provides perfect results but has a higher impact on code size. It is faster than APPROX method, and faster than the CPython equivalent implementation. It is however not available on all compilers when using FLOAT_IMPL_DOUBLE. Here is the table comparing the impact of the three conversion methods on code footprint on PYBV10 (using single-precision floats) and reversibility rate for both single-precision and double-precision floats. The table includes current situation as a baseline for the comparison: PYBV10 REPR_C FLOAT DOUBLE current = 364688 12.9% 27.6% 37.9% basic = 364812 85.6% 60.5% 85.7% approx = 365080 100.0% 98.5% 99.8% exact = 366408 100.0% 100.0% 100.0% Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
33 lines
765 B
Python
33 lines
765 B
Python
# test ffi float support
|
|
try:
|
|
import ffi
|
|
except ImportError:
|
|
print("SKIP")
|
|
raise SystemExit
|
|
|
|
|
|
def ffi_open(names):
|
|
err = None
|
|
for n in names:
|
|
try:
|
|
mod = ffi.open(n)
|
|
return mod
|
|
except OSError as e:
|
|
err = e
|
|
raise err
|
|
|
|
|
|
libm = ffi_open(("libm.so", "libm.so.6", "libc.so.0", "libc.so.6", "libc.dylib"))
|
|
|
|
# Some libc's implement tgammaf as header macro with tgamma(), so don't assume
|
|
# it'll be in library.
|
|
try:
|
|
tgammaf = libm.func("f", "tgammaf", "f")
|
|
except OSError:
|
|
print("SKIP")
|
|
raise SystemExit
|
|
|
|
for fun in (tgammaf,):
|
|
for val in (0.5, 1, 1.0, 1.5, 4, 4.0):
|
|
# limit to 5 decimals in order to pass with REPR_C with FORMAT_IMPL_BASIC
|
|
print("%.5f" % fun(val))
|