This is a tool for detecting over-sensitivity issues in programs on Unix (only tested on Linux) to accuracy of the C Math library. Some R packages come with tests of numerical results with tolerances set simply on observations on several systems at hand when the tests were written. As there is no formal accuracy requirement on C Math library, the tolerances set that way may be unreasonable and the packages may start failing on some system, when the C Math library there starts trading accuracy for performance. This tool makes the results of C Math library function less precise, it introduces errors of 1 ULP (unit of least precision), so very small errors. This work has been inspired by real problems seen on Windows, while upgrading to mingw-w64 v12 from v11. For more background, see blog posts Sensitivity to C math library and mingw-w64 v12 and Sensitivity to C math library and mingw-w64 v12 - part 2.
This tool provides wrappers of Math library functions, which can be built into a shared library, such as (works on Fedora 43):
gcc -O2 -o libmfuzz.so -shared -fPIC wrappers.c mfuzz.c -I.
The library can be then used with R by providing it to the dynamic linker
before the real C Math library, e.g. using LD_PRELOAD (on Fedora):
env LD_PRELOAD=~/mfuzz/libmfuzz.so /usr/local/bin/R
The tool is controlled by environment variables. MFUZZ_FUZZ sets which
functions are fuzzed. A common and most reasonable value is
MFUZZ_FUZZ=trans, which means that all transcendental functions will be
fuzzed - we don't want to fuzz functions that do rounding or extracting of
the integer part of a floating point number. It is also possible to
enumerate individual functions to fuzz (separated by space), which may be
useful when looking for which function a program or R package is
over-sensitive to, one might do this using interval division, iteratively.
It is also possible to explicitly disable fuzzing via MFUZZ_FUZZ=none.
Variable MFUZZ_LOG sets calls to which functions are recorded in the log
file. Functions can be specified the same way as for MFUZZ_FUZZ. The log
file path can be set via MFUZZ_LOGFILE, the default is /tmp/mfuzz.log.
To generate wrappers.c, run genwrappers.r.
The tool implementation is based on https://www.github.com/kalibera/mdebug, but unlike mdebug, which is for Windows-only, mfuzz is only for Unix.
The fuzzing in principle means that the result obtained from the Math
library in the system is shifted 1 ULP towards positive infinity unless it
would turn a negative value to a positive one, in that case, it is shift 1
ULP away from zero. The tool tries to be somewhat permissive and not return
"obviously wrong values" (writing very informally here). For example,
sin(0) is exactly 0 if the system Math library works that way. Also,
sin(-x) is exactly -sin(x). Similar constraints to fuzzing are
implemented for other functions (this is encoded in genwrappers.r). The C
standard doesn't mandate even these things, but they are implemented in
mfuzz to make the fuzzing more acceptable to mathematicians.