Part of the documentation of this fork of GAOL.
mathlib/ holds the sources of mathlib 2.1.1, the IBM Accurate Portable
Mathematical Library (libultim), with which GAOL computes its elementary
functions, as Frédéric Goualard distributes it along with GAOL:
mathlib-2.1.1.tar.gz
(SHA256 f299848aa3e57ebb6248cd3cf54ecc7661a945aeac9e420e71db194965f87281).
It is distributed under the GNU LGPL v2 or later, as the headers of its
sources state (mathlib/COPYING is the text of the GNU GPL v2). The whole
archive is kept, its own autotools build (mathlib/configure,
mathlib/Makefile.am) and its batch file for Visual C++
(mathlib/install.bat) included, which GAOL does not use.
The three builds of GAOL compile libultim from mathlib/src themselves and
install it along with GAOL, with MathLib.h and its configuration headers,
unless told to use a mathlib installed already (see
Building GAOL):
- CMake:
mathlib/CMakeLists.txt, whichCMakeLists.txtreaches withadd_subdirectory()and whichscripts/install-mathlib.shbuilds alone; - meson:
mathlib/meson.build, reached withsubdir(); - autotools:
Makefile.amof this directory, a subdirectory of GAOL's build.
They compile it with the flags of interval arithmetic GAOL is compiled with
(-frounding-math -fno-fast-math -ffp-contract=off, and -msse2 -mfpmath=sse
on 32-bit x86; /fp:strict with Visual C++). Compiled with the contraction of
multiplications and additions into fused multiply-adds, which GCC does by
default wherever the processor has them, 64-bit ARM processors included,
unless given -ffp-contract=off, mathlib reduces large arguments modulo π/2
wrongly (branred()): compiled so on x86_64, it gave cos(2^52 − 1) about
−0.4855 rather than 0.4733, and sin, cos and tan far from their values at 534
of 20080 random arguments from 2^26 to 2^54, which GAOL's bounds did not
enclose. This is why the builds refuse an installed mathlib whose cosine of
2^52 − 1 is wrong (see The three builds). mathlib
is also compiled with -w, whatever warning flags the project building GAOL
gives: with -Wall -Wextra -Wconversion, GCC 9 printed 25255 warnings over
its sources.
These build files were added for GAOL, with mathlib/mathlib_configuration.h.in,
the configuration header mathlib's own configure script would write, which
every build copies as it is: the macros describing the target are set by the
preprocessor there, from the macros of the compiler, which works the same in a
cross-compilation and with Visual C++. src/mathlib_config_msvc.h and
src/mathlib_config_mingw.h, the configuration files src/mathlib_config.h
includes for Visual C++ and MinGW, are replaced with files including that one.
mathlib/CMakeLists.txt and mathlib/mathlib_configuration.h.in derive from
the CMake build of the same mathlib in IBEX
(interval_lib_wrapper/gaol/3rd/mathlib-2.1.1/, Cyril Bouvier and Gilles
Chabert).
The sources are those of the archive but for the fixes below, each a change
of a few lines in one file, with the pragma of the tables. Every one is
covered by the tests of tests/ (see tests/elementary_values.py), and
What differs from GAOL lists them among the changes
of the fork.
src/sincos32.c, the cosine. For the arguments hardest to round,ucos()falls back onmpcos(), which computes cos(x) with multiple-precision numbers, as sin(π/2 − x) when x > 0.8.c32(x, y, z)sets y to cos(x) and z to sin(x), andmpcos()took the cosine of π/2 − x, which is sin(x), rather than its sine:ucos()returned sin(x), and GAOL bounds not enclosing cos(x), at 54 of the hard-to-round arguments of cos of CORE-MATH, all between 0.80 and 0.853 (dreal-deps/mathlib#2). The call is nowc32(&b,&c,&a,p). glibc, which took the same code from IBM, fixed the same line in 2003 (Debian bug 153548, commit).src/mpsqrt.c, the square root in multiple precision, which the arctangent uses at the arguments hardest to round (src/mpatan.c,src/mpatan2.c).fastiroot(), which gives it a first approximation of 1/√x, read and wrote the halves of a double through an array of twolongs: wherelonghas 64 bits (Linux and macOS on 64-bit processors),p.i[HIGH_HALF]is not the high half of the double, x was not scaled to [0.5, 2), and the approximation was wrong.atan()then returned values far from atan(x), or had not returned after 20 ms, at 12003 and 3967 of the 55190 hard-to-round arguments of atan of CORE-MATH on x86_64: atan(1.016527294692847) was 0.082 instead of 0.794. They areints, as glibc made them in 2003 (Michael Matz, "fastiroot: Fix 64-bit problem", commit); Debian bug 210613 is the same bug on Alpha, whose three arguments fail on x86_64 without this fix.src/ulog.c, the logarithm at subnormal arguments.ulog()scales a subnormal x by 2^54, which its exponent n accounts for, but its last, multiple-precision stage computed the logarithm from the scaled x and from an approximation of the logarithm of the unscaled one:ulog()returned about 2^54 at 26 of the 53 subnormal hard-to-round arguments of log of CORE-MATH on x86_64: log(0x0.8819864d7985dp-1022) was 1.8e16 instead of −709.03. That stage is given the unscaled x, kept inx0. glibc kept it in__ieee754_loguntil it removed the multiple-precision stages in 2018 (Wilco Dijkstra, "Remove slow paths from log", commit).src/utan.c, the tangent. In 7 of the tests with whichutan()decides whether its result is rounded right, the error boundt4was assigned in one operand of==and read in the other, with no sequence point between them: undefined behaviour, which Clang warns about (-Wunsequenced). GCC and Clang 18, from-O0to-O3, gave the same results as witht4assigned before the test, at the 1077348 hard-to-round arguments of tan of CORE-MATH and 10 million others.t4is assigned before the test, as glibc did in 2009 (Ulrich Drepper, "Fix -Wsequence-point warnings", commit) and Fabrice Le Bars in his fork of mathlib (commit).src/halfulp.c, the shifts.halfulp(), whichupow()calls for the powers that may be exact, counts the significant bits of the high half of a double by shifting it leftward until it is 0, as anint: shifting a positiveintinto its sign bit is undefined behaviour in C, which the UndefinedBehaviorSanitizer of Clang reports ("left shift of 1070596096 by 12 places cannot be represented in type 'int4'"), and which the continuous integration suppressed (issue #5). Anunsigned intis shifted instead, which gives the bits the compilers gave:halfulp()returns the same doubles at 4 million arguments, 228644 of them exact powers, and the tests pass with Clang 18 and no suppression.src/sincos32.h, the include guard, which testedSINCOS32_Hbut definedSINCCOS32_H, which Clang warns about (-Wheader-guard): it definesSINCOS32_H, as Fabrice Le Bars fixed it (commit).src/mathlib_config.h,#pragma STDC FENV_ACCESS ONat its end, which every source of mathlib includes: the pragma of C99 (7.6.1) that tells the compiler the code may be executed with a rounding direction other than the default, and that it must not fold nor reorder its floating-point operations as if the rounding were to nearest. GAOL sets the rounding direction to nearest before calling mathlib and back upward afterwards, and mathlib is compiled with the flags of interval arithmetic: the pragma states for the compiler what those flags ask of it, as Fabrice Le Bars does in his fork (commit), with the uppercaseONthat macOS warns about in the lowercase. Clang 18 honours it, and the elementary functions of an interval took the same time with it as without (exp, log, sin and cos within 0.8%, below the dispersion of the measures); GCC 13 ignores it and gave alibultim.aidentical byte for byte; Visual C++ is given/fp:strict, which its documentation says makes it behave as iffenv_access(on)were set, and reads the pragma for Visual C++ rather than the one of C99, which it does not know.src/uatan.tbl,src/ulog.tblandsrc/utan.tbl,#pragma GCC diagnostic ignored "-Wmissing-braces". The entries of the tables, of the union typenumber, are written{0x3ff6a13c, 0xd1537290 }where the union holds an array: GCC and Clang warn about each of them with-Wall(-Wmissing-braces), 15777 times over mathlib's sources, and printing them is slow enough to stop a build: compilingsrc/atnat.cwith-Wall, which includessrc/uatan.tbland its 6027 of them, had not finished after ten minutes, against 0.22 s without. The three tables carry 15165 of the 15777 and are given the pragma, as Fabrice Le Bars does in his fork (commit): 434 are left, from the tables of the other sources, kept as they are, which-wturns off in the builds of GAOL. The pragma changes no code:libultim.ais the same, byte for byte, with it and without it.src/AARCH64_DPChange.c,Init_Lib()andExit_Lib(), whichmathlib/mathlib_configuration.h.inchooses for every target (MATHLIB_AARCH64): nothing in it is specific to ARM64, and it goes through<fenv.h>alone.Init_Lib()restored the default floating-point environment (FESETENV(FE_DFL_ENV)) and returned 0, andExit_Lib()did nothing: the rounding direction the caller had set was lost, where the comment ofInit_Lib()says its result is whatExit_Lib()takes to restore it.Init_Lib()now sets the rounding direction to nearest, which mathlib's algorithms need, and returns the one it found, andExit_Lib()sets that one back, as the fork of mathlib by Fabrice Le Bars does (commit); the rest of the floating-point environment, whose exception flags and masksFE_DFL_ENValso reset, is left as it is. Nothing is kept in the global variables of the file, which no longer has any use for them: two threads callinggaol::init()no longer write the same variables. The versions mathlib's own build chooses for the other processors are not used:src/LINUX64_DPChange.c(x86_64 Linux),src/LINUX_DPChange.c(32-bit x86) andsrc/IX86MACOSX_DPChange.c(Intel Macs) save the control word of the x87 unit in global variables, which two threads callinggaol::init()would write at once, to set a precision that GAOL loses right after by restoring the default floating-point environment, and that the doubles of 32-bit x86, computed in SSE2, do not depend on;src/LINUX_DPChange.candsrc/MSVC_DPChange.care 32-bit x86 assembly besides.
Should a new archive be taken, unpack it over mathlib/, keep
mathlib/CMakeLists.txt, mathlib/meson.build and
mathlib/mathlib_configuration.h.in, and apply the changes above again:
git diff of the files of mathlib/src before the update shows each of them
exactly.