From 24ea440a4b44938e2126c3cbfe1de75610844e39 Mon Sep 17 00:00:00 2001 From: Jordan08 Date: Mon, 5 Oct 2026 23:07:21 +0200 Subject: [PATCH 1/5] gaol_config.h, doc, .github/audit, tests: -ffinite-math-only refusal and compile tests --- .github/audit/compare.py | 2 +- .github/audit/make_probe.py | 2 +- doc/building.md | 2 +- doc/three-builds.md | 4 ++-- doc/using.md | 2 +- examples/examples.md | 6 +++--- gaol/gaol_config.h | 5 +++-- tests/CMakeLists.txt | 10 +++++++++- 8 files changed, 21 insertions(+), 12 deletions(-) diff --git a/.github/audit/compare.py b/.github/audit/compare.py index f8680611..82a2888d 100644 --- a/.github/audit/compare.py +++ b/.github/audit/compare.py @@ -23,7 +23,7 @@ 'GAOL_HAVE_GETRUSAGE', 'GAOL_HAVE_ROUNDING_MATH_OPTION', 'GAOL_HAVE_VISIBILITY_OPTIONS', 'GAOL_USING_SSE2_INSTRUCTIONS', 'GAOL_USING_SSE3_INSTRUCTIONS', 'GAOL_WORDS_BIGENDIAN', 'GAOL_RND_PROBE', 'GAOL_HAVE_AVX512_TARGET', 'GAOL_PREFER_AVX512', - 'NDEBUG', '_GLIBCXX_ASSERTIONS', '__FAST_MATH__', '__OPTIMIZE__', '__SSE2_MATH__', '__STRICT_ANSI__') + 'NDEBUG', '_GLIBCXX_ASSERTIONS', '__FAST_MATH__', '__FINITE_MATH_ONLY__', '__OPTIMIZE__', '__SSE2_MATH__', '__STRICT_ANSI__') # The macros compared by value VALUES = ('GAOL_SIZEOF_INT', 'GAOL_SIZEOF_LONG_LONG_INT', 'FLT_EVAL_METHOD', '__cplusplus', 'GAOL_MAJOR_VERSION', 'GAOL_MINOR_VERSION', 'GAOL_MICRO_VERSION', 'GAOL_VERSION') diff --git a/.github/audit/make_probe.py b/.github/audit/make_probe.py index da2cbd0c..c3d825de 100644 --- a/.github/audit/make_probe.py +++ b/.github/audit/make_probe.py @@ -12,7 +12,7 @@ 'GAOL_USING_SSE2_INSTRUCTIONS', 'GAOL_USING_SSE3_INSTRUCTIONS', 'GAOL_WORDS_BIGENDIAN', 'GAOL_RND_PROBE', 'GAOL_RND_SSE_REGISTER'] # Compared by value only (GAOL_VERSION is a string, which #if cannot read) text = ['GAOL_INLINE', 'GAOL_PUBLIC', 'GAOL_MAJOR_VERSION', 'GAOL_MINOR_VERSION', 'GAOL_MICRO_VERSION', 'GAOL_VERSION'] -compiler = ['__cplusplus', '__STRICT_ANSI__', '__OPTIMIZE__', 'NDEBUG', '_GLIBCXX_ASSERTIONS', '__FAST_MATH__', 'FLT_EVAL_METHOD', +compiler = ['__cplusplus', '__STRICT_ANSI__', '__OPTIMIZE__', 'NDEBUG', '_GLIBCXX_ASSERTIONS', '__FAST_MATH__', '__FINITE_MATH_ONLY__', 'FLT_EVAL_METHOD', '__SSE2__', '__SSE3__', '__SSE2_MATH__', '__PIC__', '__x86_64__', '__i386__', '__aarch64__', '__arm__', '__APPLE__', '__linux__', '_WIN32', '__MINGW64_VERSION_MAJOR'] lines = ['#include ', '#include "gaol/gaol.h"'] diff --git a/doc/building.md b/doc/building.md index 9ceb7fbe..eaf078d9 100644 --- a/doc/building.md +++ b/doc/building.md @@ -300,7 +300,7 @@ The three builds have the same targets: CppUnit: they are in `tests/` now, and need nothing (see [The tests](tests.md)). `ctest` runs the tests and the examples of a CMake build; `make test` leaves the examples out (label `example`, CMake 3.17 and - later). With meson before 0.57, `meson test` runs the examples too, and + later) and also runs the compile tests `refused_*` and `nodiscard_*`. With meson before 0.57, `meson test` runs the examples too, and `meson test --suite unit` the tests alone. - **The examples** check what they print and fail otherwise (see [examples/examples.md](../examples/examples.md)). diff --git a/doc/three-builds.md b/doc/three-builds.md index b1958af7..6f338221 100644 --- a/doc/three-builds.md +++ b/doc/three-builds.md @@ -155,8 +155,8 @@ alone refuses, when compiling: `-fno-fast-math`, one of the flags of interval arithmetic, turns `-ffast-math` and `-ffinite-math-only` off when it comes after them on the command line, and -not when it comes before: `gaol.pc` and `gaol::gaol` give it, and the code -including GAOL's headers is refused when the option follows it. The CMake tests +not when `-ffast-math` or `-ffinite-math-only` comes before it: `gaol.pc` and `gaol::gaol` give it, and the code +including GAOL's headers is refused when `-ffast-math` or `-ffinite-math-only` follows it. The CMake tests `refused_finite_math_only` and `refused_fast_math` compile `tests/refused_options.cpp` with each of the two options, after the flags of interval arithmetic, and check that `gaol/gaol_config.h` refuses it (GCC and diff --git a/doc/using.md b/doc/using.md index fb56e76e..b7bfc96c 100644 --- a/doc/using.md +++ b/doc/using.md @@ -31,7 +31,7 @@ what contradicts them, `-ffast-math`, `-ffinite-math-only` and doubles computed on the x87 unit (see [Compilers and options refused](three-builds.md#compilers-and-options-refused)). `-fno-fast-math` turns the first two off when it comes after them on the -command line, and does nothing when it comes before them, where the +command line, and does nothing when `-ffast-math` or `-ffinite-math-only` comes before it, where the compilation stops. With `-frounding-math` on x86, GCC 12.1 to 12.3 and 13.1 to 13.2 (Debian 12 diff --git a/examples/examples.md b/examples/examples.md index cb8dd4bc..ae7590e7 100644 --- a/examples/examples.md +++ b/examples/examples.md @@ -1072,9 +1072,9 @@ check: #endif ``` -and a row in the refused options of `doc/three-builds.md`. Test: a compile -test with `PASS_REGULAR_EXPRESSION "ffinite-math-only"`, as -`tests/fp_strict` does for other options. +and a row in the refused options of `doc/three-builds.md`. Test: a +`try_compile()` at configure time, as `tests/fp_strict` does for other +options (Visual C++ only). **5. `atanh([1, x])`.** In `atanh()`, return the empty set when `J.left() == 1.0 || J.right() == -1.0` (J being `I & [-1, 1]`), and write the diff --git a/gaol/gaol_config.h b/gaol/gaol_config.h index 5127dac7..a2ad5ec9 100644 --- a/gaol/gaol_config.h +++ b/gaol/gaol_config.h @@ -196,8 +196,9 @@ /* -ffinite-math-only, which -ffast-math and -Ofast turn on, has the compiler take NaN and infinities never to occur, in the inline functions of GAOL's headers as anywhere else: the empty interval has NaN bounds, and is_empty() - reads it as !(left() <= right()). The compiler then folds the test away, and - ([1, 2] & [3, 4]).is_empty() is false (GCC 9, Clang 18): the tests + reads it as !std::islessequal(left(), right()). The compiler then folds the test away, and + ([1, 2] & [3, 4]).is_empty() is false with -ffinite-math-only: with GCC 13 at -O0, -O2 and -O3, + and with Clang 18 at -O0, and at -O2 and -O3 only with volatile bounds: the tests refused_finite_math_only and refused_fast_math (tests/CMakeLists.txt) check the refusal. GCC and Clang define __FINITE_MATH_ONLY__, as 0 or 1; Visual C++ defines nothing of the kind (its /fp:fast is refused below). This also diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8f36f491..de9ccc95 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -136,7 +136,7 @@ set_tests_properties(version_file PROPERTIES LABELS unit) function(gaol_refused_test name flag text) add_library(gaol_${name} OBJECT refused_options.cpp) set_target_properties(gaol_${name} PROPERTIES EXCLUDE_FROM_ALL TRUE) - target_include_directories(gaol_${name} PRIVATE "${CMAKE_BINARY_DIR}" "${CMAKE_SOURCE_DIR}") + target_link_libraries(gaol_${name} PRIVATE gaol::gaol) target_compile_options(gaol_${name} PRIVATE ${GAOL_INTERVAL_FLAGS} ${GAOL_FMA_FLAGS} ${flag}) add_test(NAME ${name} COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config $ --target gaol_${name}) @@ -150,6 +150,14 @@ if(NOT MSVC AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") "GAOL cannot be compiled with -ffinite-math-only") gaol_refused_test(refused_fast_math -ffast-math "GAOL cannot be compiled with -ffast-math") + # Positive witness: compilation succeeds without refused options + add_library(gaol_refused_positive OBJECT refused_options.cpp) + set_target_properties(gaol_refused_positive PROPERTIES EXCLUDE_FROM_ALL TRUE) + target_link_libraries(gaol_refused_positive PRIVATE gaol::gaol) + target_compile_options(gaol_refused_positive PRIVATE ${GAOL_INTERVAL_FLAGS} ${GAOL_FMA_FLAGS}) + add_test(NAME refused_positive + COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config $ --target gaol_refused_positive) + set_tests_properties(refused_positive PROPERTIES LABELS unit RESOURCE_LOCK gaol_compile_tests) endif() # The warning of the CPack block of CMakeLists.txt, when the configure that the From 5c2ca509286c1a98182c65aebac96774e8ecfcb2 Mon Sep 17 00:00:00 2001 From: Jordan08 Date: Mon, 5 Oct 2026 23:14:39 +0200 Subject: [PATCH 2/5] tests: add refused_* compile tests to autotools and meson --- tests/Makefile.am | 28 ++++++++++++++++++++++++++++ tests/meson.build | 20 ++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/tests/Makefile.am b/tests/Makefile.am index 16b651e0..116282f8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -49,6 +49,34 @@ endif if WITH_TESTS noinst_PROGRAMS = $(GAOL_TESTS) gaol_performance TESTS = $(GAOL_TESTS) +# Compile tests: refused_options.cpp with -ffinite-math-only and -ffast-math +# (GAOL v5). autotools builds GAOL with -frounding-math and its link option, +# whose -fno-fast-math turns -ffinite-math-only off when it comes after it, +# so these options come before the flags of interval arithmetic. The test +# passes if the compilation fails with the message of gaol_config.h. +# No such test for Visual C++, which has neither option nor macro. +if HOST_LINUX +# GCC and Clang on Linux refuse -ffinite-math-only and -ffast-math +check-refused_finite_math_only: + @echo "Testing -ffinite-math-only refusal..." + @$(CXX) $(DEFS) -ffinite-math-only $(AM_CPPFLAGS) $(CPPFLAGS) $(CXXFLAGS) \ + -I$(top_builddir) -I$(top_srcdir) -c $(top_srcdir)/tests/refused_options.cpp -o /dev/null 2>&1 | \ + grep -q "GAOL cannot be compiled with -ffinite-math-only" || exit 1 + +check-refused_fast_math: + @echo "Testing -ffast-math refusal..." + @$(CXX) $(DEFS) -ffast-math $(AM_CPPFLAGS) $(CPPFLAGS) $(CXXFLAGS) \ + -I$(top_builddir) -I$(top_srcdir) -c $(top_srcdir)/tests/refused_options.cpp -o /dev/null 2>&1 | \ + grep -q "GAOL cannot be compiled with -ffast-math" || exit 1 + +# Positive witness: compilation succeeds without refused options +check-refused_positive: + @echo "Testing compilation without refused options..." + @$(CXX) $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(CXXFLAGS) \ + -I$(top_builddir) -I$(top_srcdir) -c $(top_srcdir)/tests/refused_options.cpp -o /dev/null + +TESTS += check-refused_finite_math_only check-refused_fast_math check-refused_positive +endif else check-local: @echo "No unit tests: configure with --with-tests" diff --git a/tests/meson.build b/tests/meson.build index 1a484070..bf5001f1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -41,6 +41,26 @@ tests_dep = declare_dependency( link_args: gaol_link_args, dependencies: deps, ) +# Compile tests: refused_options.cpp with -ffinite-math-only and -ffast-math +# (GAOL v5). Not with Visual C++, which has neither option nor macro. +if not msvc + # Test -ffinite-math-only refusal + test('refused_finite_math_only', + compile: ['-ffinite-math-only', '-c', 'refused_options.cpp'], + should_fail: true, + stdout: 'GAOL cannot be compiled with -ffinite-math-only', + dependencies: tests_dep) + # Test -ffast-math refusal + test('refused_fast_math', + compile: ['-ffast-math', '-c', 'refused_options.cpp'], + should_fail: true, + stdout: 'GAOL cannot be compiled with -ffast-math', + dependencies: tests_dep) +endif +# Positive witness: compilation succeeds without refused options +test('refused_positive', + compile: ['-c', 'refused_options.cpp'], + dependencies: tests_dep) foreach t : gaol_tests # fast_math_link: -ffast-math to its link only, its source being compiled as # any code using GAOL, and GAOL_TESTS_NO_DAZ_FTZ where its link has From ab3c932a30aa6b62340010a9d354ae0b44787ad4 Mon Sep 17 00:00:00 2001 From: Jordan08 Date: Tue, 6 Oct 2026 00:28:42 +0200 Subject: [PATCH 3/5] tests/refused_options.sh: the refused_* tests in autotools and meson, CMake's as nodiscard_* --- doc/building.md | 3 ++- doc/continuous-integration.md | 4 +-- doc/tests.md | 30 ++++++++++++++--------- tests/CMakeLists.txt | 36 +++++++++++++-------------- tests/Makefile.am | 39 ++++++++--------------------- tests/Makefile.in | 34 ++++++++++++++++---------- tests/meson.build | 43 +++++++++++++++++--------------- tests/refused_options.cpp | 10 +++++--- tests/refused_options.sh | 46 +++++++++++++++++++++++++++++++++++ 9 files changed, 145 insertions(+), 100 deletions(-) create mode 100644 tests/refused_options.sh diff --git a/doc/building.md b/doc/building.md index eaf078d9..d371a8a4 100644 --- a/doc/building.md +++ b/doc/building.md @@ -300,7 +300,8 @@ The three builds have the same targets: CppUnit: they are in `tests/` now, and need nothing (see [The tests](tests.md)). `ctest` runs the tests and the examples of a CMake build; `make test` leaves the examples out (label `example`, CMake 3.17 and - later) and also runs the compile tests `refused_*` and `nodiscard_*`. With meson before 0.57, `meson test` runs the examples too, and + later), and runs the compile tests `refused_*` and `nodiscard_*` too. With + meson before 0.57, `meson test` runs the examples too, and `meson test --suite unit` the tests alone. - **The examples** check what they print and fail otherwise (see [examples/examples.md](../examples/examples.md)). diff --git a/doc/continuous-integration.md b/doc/continuous-integration.md index d3884385..2f955a5d 100644 --- a/doc/continuous-integration.md +++ b/doc/continuous-integration.md @@ -65,8 +65,8 @@ refuse Clang on 32-bit ARM and Clang 14 on 64-bit ARM, that `gaol/gaol_config.h` refuses MinGW-w64 GCC 11 to 13 and MSYS2 MINGW64 (GCC and Clang) on x64 and GCC 11 on x86, `-ffinite-math-only` and `-ffast-math` with GCC and Clang (the tests -`refused_finite_math_only` and `refused_fast_math` of `make test`), and Visual -C++ and clang-cl without `/fp:strict`. Jobs of each build +`refused_*` of the three builds, see [Tests](tests.md)), and Visual C++ and +clang-cl without `/fp:strict`. Jobs of each build restore the rounding direction (`GAOL_PRESERVE_ROUNDING`): Ubuntu x86_64 GCC and arm64 Clang, Debian i386 and armhf, macOS arm64, Visual Studio x64, autotools and meson. The jobs built in Release print the time per operation in diff --git a/doc/tests.md b/doc/tests.md index 36d211b9..21c55474 100644 --- a/doc/tests.md +++ b/doc/tests.md @@ -675,20 +675,26 @@ mode there. and read a file holding NUL bytes, meson stripped the blanks of Unicode, autoconf dropped the blanks within the version and let NUL bytes through, took `5.0.0)` for 5.0.0 and stopped on a bracket) (GAOL v5). -- **`refused_finite_math_only` and `refused_fast_math`:** compile tests, made - by the CMake build where the compiler is GCC or Clang. `tests/refused_options.cpp`, - a program including ``, is compiled with `-ffinite-math-only` and +- **`refused_finite_math_only`, `refused_fast_math` and `refused_positive`** + (CMake build), **`refused_options.sh`** (autotools build) and + **`refused_options`** (meson build): compile tests, where the compiler is GCC + or Clang (the script skips another one). `tests/refused_options.cpp`, a + program including ``, is compiled with `-ffinite-math-only` and with `-ffast-math`, put after the flags of interval arithmetic, and the compilation has to fail with the message of `gaol/gaol_config.h`, which the - test looks for in the output of the build. With `-ffinite-math-only` the - compiler takes NaN and infinities never to occur, in the inline functions of - the headers too, and the empty interval, whose bounds are NaN, is no longer - told empty: `([1, 2] & [3, 4]).is_empty()` was false with GCC 9 and Clang 18. - The header did not refuse `-ffinite-math-only`, nor `-Ofast` or `-ffast-math` - followed by `-frounding-math` with Clang, which leave `__FAST_MATH__` - undefined and `__FINITE_MATH_ONLY__` at 1 (GAOL v5). The autotools and meson - builds have no such test, the header being the same; `tests/fp_strict` is - the check of Visual C++ without `/fp:strict`. + test looks for in the output of the compiler; compiled with neither, it has + to compile. The CMake build makes each compilation a target of its own, + built by the test; `tests/refused_options.sh` runs the three compilations in + the autotools and meson builds, with the compiler and the flags of the code + using GAOL. With `-ffinite-math-only` the compiler takes NaN and infinities + never to occur, in the inline functions of the headers too, and the empty + interval, whose bounds are NaN, is no longer told empty: + `([1, 2] & [3, 4]).is_empty()` was false with GCC 9.4 and 13 at `-O0`, `-O2` + and `-O3`, and with Clang 18 at `-O0`, and at `-O2` and `-O3` when the bounds + are `volatile`. The header did not refuse `-ffinite-math-only`, nor `-Ofast` + or `-ffast-math` followed by `-frounding-math` with Clang, which leave + `__FAST_MATH__` undefined and `__FINITE_MATH_ONLY__` at 1 (GAOL v5). + `tests/fp_strict` is the check of Visual C++ without `/fp:strict`. - **`cpack_stale_configure`** (CMake build, on a Unix system that builds for itself, where the tree has a `configure`): a script, not a program (`tests/cpack_stale_configure.cmake`). CPack puts `configure` in the archive diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index de9ccc95..626466c5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -121,43 +121,41 @@ set_tests_properties(version_file PROPERTIES LABELS unit) # them (GAOL v5): the tests refused_finite_math_only and refused_fast_math # compile refused_options.cpp, a program using GAOL, with -ffinite-math-only # and with -ffast-math, and the compilation has to fail with the message of the -# refusal. The object is a target of its own, left out of the build of GAOL and -# made by the test (cmake --build), so that it gets the compiler and the flags -# of the platform as the files of GAOL do. A build fails for many reasons, so -# the test does not look at its exit status: it passes if the output of the -# build holds the message. +# refusal; refused_positive compiles it with neither, and the compilation has +# to succeed. The object is a target of its own, left out of the build of GAOL +# and made by the test (cmake --build), so that it gets the compiler and the +# flags of the platform as the files of GAOL do. A build fails for many +# reasons, so the tests of a refused option do not look at its exit status: +# they pass if the output of the build holds the message. # # The option has to come after the flags of interval arithmetic, whose # -fno-fast-math turns it off otherwise. gaol::gaol is therefore not linked, # CMake putting the options of a linked library after those of the target: the -# flags it carries are written out here. Not with Visual C++, which has neither -# option nor macro (see doc/three-builds.md). The autotools and meson builds -# have no such test: the header it checks is the same. +# flags, the definitions and the include directories it carries are written +# out here, as for the tests nodiscard_* below. Not with Visual C++, which has +# neither option nor macro (see doc/three-builds.md). The autotools and meson +# builds run the same checks with tests/refused_options.sh. function(gaol_refused_test name flag text) add_library(gaol_${name} OBJECT refused_options.cpp) set_target_properties(gaol_${name} PROPERTIES EXCLUDE_FROM_ALL TRUE) - target_link_libraries(gaol_${name} PRIVATE gaol::gaol) + target_include_directories(gaol_${name} PRIVATE "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}") target_compile_options(gaol_${name} PRIVATE ${GAOL_INTERVAL_FLAGS} ${GAOL_FMA_FLAGS} ${flag}) + target_compile_definitions(gaol_${name} PRIVATE $) add_test(NAME ${name} COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config $ --target gaol_${name}) # One build at a time in the build directory, whatever ctest -j says: the # lock of every compile test - set_tests_properties(${name} PROPERTIES LABELS unit RESOURCE_LOCK gaol_compile_tests - PASS_REGULAR_EXPRESSION "${text}") + set_tests_properties(${name} PROPERTIES LABELS unit RESOURCE_LOCK gaol_compile_tests) + if(text) + set_tests_properties(${name} PROPERTIES PASS_REGULAR_EXPRESSION "${text}") + endif() endfunction() if(NOT MSVC AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") gaol_refused_test(refused_finite_math_only -ffinite-math-only "GAOL cannot be compiled with -ffinite-math-only") gaol_refused_test(refused_fast_math -ffast-math "GAOL cannot be compiled with -ffast-math") - # Positive witness: compilation succeeds without refused options - add_library(gaol_refused_positive OBJECT refused_options.cpp) - set_target_properties(gaol_refused_positive PROPERTIES EXCLUDE_FROM_ALL TRUE) - target_link_libraries(gaol_refused_positive PRIVATE gaol::gaol) - target_compile_options(gaol_refused_positive PRIVATE ${GAOL_INTERVAL_FLAGS} ${GAOL_FMA_FLAGS}) - add_test(NAME refused_positive - COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config $ --target gaol_refused_positive) - set_tests_properties(refused_positive PROPERTIES LABELS unit RESOURCE_LOCK gaol_compile_tests) + gaol_refused_test(refused_positive "" "") endif() # The warning of the CPack block of CMakeLists.txt, when the configure that the diff --git a/tests/Makefile.am b/tests/Makefile.am index 116282f8..1efc7197 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -46,37 +46,18 @@ else fast_math_link_CPPFLAGS = $(AM_CPPFLAGS) endif +# refused_options.sh: the options gaol/gaol_config.h refuses, which the +# compile tests refused_* of tests/CMakeLists.txt check in the CMake build +# (GAOL v5), compiled with the compiler and the flags of the tests +TEST_EXTENSIONS = .sh +SH_LOG_COMPILER = $(SHELL) +AM_TESTS_ENVIRONMENT = GAOL_CXX='$(CXX)'; \ + GAOL_CXXFLAGS='$(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)'; \ + export GAOL_CXX GAOL_CXXFLAGS; + if WITH_TESTS noinst_PROGRAMS = $(GAOL_TESTS) gaol_performance -TESTS = $(GAOL_TESTS) -# Compile tests: refused_options.cpp with -ffinite-math-only and -ffast-math -# (GAOL v5). autotools builds GAOL with -frounding-math and its link option, -# whose -fno-fast-math turns -ffinite-math-only off when it comes after it, -# so these options come before the flags of interval arithmetic. The test -# passes if the compilation fails with the message of gaol_config.h. -# No such test for Visual C++, which has neither option nor macro. -if HOST_LINUX -# GCC and Clang on Linux refuse -ffinite-math-only and -ffast-math -check-refused_finite_math_only: - @echo "Testing -ffinite-math-only refusal..." - @$(CXX) $(DEFS) -ffinite-math-only $(AM_CPPFLAGS) $(CPPFLAGS) $(CXXFLAGS) \ - -I$(top_builddir) -I$(top_srcdir) -c $(top_srcdir)/tests/refused_options.cpp -o /dev/null 2>&1 | \ - grep -q "GAOL cannot be compiled with -ffinite-math-only" || exit 1 - -check-refused_fast_math: - @echo "Testing -ffast-math refusal..." - @$(CXX) $(DEFS) -ffast-math $(AM_CPPFLAGS) $(CPPFLAGS) $(CXXFLAGS) \ - -I$(top_builddir) -I$(top_srcdir) -c $(top_srcdir)/tests/refused_options.cpp -o /dev/null 2>&1 | \ - grep -q "GAOL cannot be compiled with -ffast-math" || exit 1 - -# Positive witness: compilation succeeds without refused options -check-refused_positive: - @echo "Testing compilation without refused options..." - @$(CXX) $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(CXXFLAGS) \ - -I$(top_builddir) -I$(top_srcdir) -c $(top_srcdir)/tests/refused_options.cpp -o /dev/null - -TESTS += check-refused_finite_math_only check-refused_fast_math check-refused_positive -endif +TESTS = $(GAOL_TESTS) refused_options.sh else check-local: @echo "No unit tests: configure with --with-tests" diff --git a/tests/Makefile.in b/tests/Makefile.in index f4ca5ea0..31d51b45 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -114,7 +114,7 @@ host_triplet = @host@ @HOST_LINUX_TRUE@am__append_1 = automatic_cleanup @WITH_TESTS_TRUE@noinst_PROGRAMS = $(am__EXEEXT_2) \ @WITH_TESTS_TRUE@ gaol_performance$(EXEEXT) -@WITH_TESTS_TRUE@TESTS = $(am__EXEEXT_2) +@WITH_TESTS_TRUE@TESTS = $(am__EXEEXT_2) refused_options.sh subdir = tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ @@ -582,7 +582,6 @@ AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' RECHECK_LOGS = $(TEST_LOGS) AM_RECURSIVE_TARGETS = check recheck TEST_SUITE_LOG = test-suite.log -TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) am__set_b = \ @@ -597,10 +596,9 @@ am__set_b = \ esac am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) -TEST_LOGS = $(am__test_logs2:.test.log=.log) -TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver -TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ - $(TEST_LOG_FLAGS) +TEST_LOGS = $(am__test_logs2:.sh.log=.log) +SH_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +SH_LOG_COMPILE = $(SH_LOG_COMPILER) $(AM_SH_LOG_FLAGS) $(SH_LOG_FLAGS) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ @@ -765,13 +763,23 @@ GAOL_TESTS = arithmetic elementary rounding_direction numbers \ fast_math_link_LDFLAGS = $(AM_LDFLAGS) -ffast-math @GAOL_NO_DAZ_FTZ_FALSE@fast_math_link_CPPFLAGS = $(AM_CPPFLAGS) @GAOL_NO_DAZ_FTZ_TRUE@fast_math_link_CPPFLAGS = $(AM_CPPFLAGS) -DGAOL_TESTS_NO_DAZ_FTZ=1 + +# refused_options.sh: the options gaol/gaol_config.h refuses, which the +# compile tests refused_* of tests/CMakeLists.txt check in the CMake build +# (GAOL v5), compiled with the compiler and the flags of the tests +TEST_EXTENSIONS = .sh +SH_LOG_COMPILER = $(SHELL) +AM_TESTS_ENVIRONMENT = GAOL_CXX='$(CXX)'; \ + GAOL_CXXFLAGS='$(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)'; \ + export GAOL_CXX GAOL_CXXFLAGS; + gaol_performance_SOURCES = performance.cpp CLEANFILES = gaol_bench$(EXEEXT) BASH = bash all: all-am .SUFFIXES: -.SUFFIXES: .cpp .lo .log .o .obj .test .test$(EXEEXT) .trs +.SUFFIXES: .cpp .lo .log .o .obj .sh .sh$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -1446,19 +1454,19 @@ automatic_cleanup.log: automatic_cleanup$(EXEEXT) --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) -.test.log: +.sh.log: @p='$<'; \ $(am__set_b); \ - $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + $(am__check_pre) $(SH_LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ - $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + $(am__common_driver_flags) $(AM_SH_LOG_DRIVER_FLAGS) $(SH_LOG_DRIVER_FLAGS) -- $(SH_LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) -@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@.sh$(EXEEXT).log: @am__EXEEXT_TRUE@ @p='$<'; \ @am__EXEEXT_TRUE@ $(am__set_b); \ -@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(SH_LOG_DRIVER) --test-name "$$f" \ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ -@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_SH_LOG_DRIVER_FLAGS) $(SH_LOG_DRIVER_FLAGS) -- $(SH_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) @WITH_TESTS_TRUE@check-local: check-am: all-am diff --git a/tests/meson.build b/tests/meson.build index bf5001f1..70a6a68a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -41,26 +41,6 @@ tests_dep = declare_dependency( link_args: gaol_link_args, dependencies: deps, ) -# Compile tests: refused_options.cpp with -ffinite-math-only and -ffast-math -# (GAOL v5). Not with Visual C++, which has neither option nor macro. -if not msvc - # Test -ffinite-math-only refusal - test('refused_finite_math_only', - compile: ['-ffinite-math-only', '-c', 'refused_options.cpp'], - should_fail: true, - stdout: 'GAOL cannot be compiled with -ffinite-math-only', - dependencies: tests_dep) - # Test -ffast-math refusal - test('refused_fast_math', - compile: ['-ffast-math', '-c', 'refused_options.cpp'], - should_fail: true, - stdout: 'GAOL cannot be compiled with -ffast-math', - dependencies: tests_dep) -endif -# Positive witness: compilation succeeds without refused options -test('refused_positive', - compile: ['-c', 'refused_options.cpp'], - dependencies: tests_dep) foreach t : gaol_tests # fast_math_link: -ffast-math to its link only, its source being compiled as # any code using GAOL, and GAOL_TESTS_NO_DAZ_FTZ where its link has @@ -84,6 +64,29 @@ foreach t : gaol_tests check_targets += [exe] endforeach +# The options gaol/gaol_config.h refuses, which the compile tests refused_* of +# tests/CMakeLists.txt check in the CMake build (GAOL v5): refused_options.sh +# compiles refused_options.cpp with the compiler, the cpp_args and the language +# standard of the build, and the flags gaol.pc gives the code using GAOL. Not +# with Visual C++, which has neither option nor macro, nor clang-cl, which +# ignores the options (gaol_config.h refuses the /fp:fast of both). +if not msvc + refused_args = get_option('cpp_args') + if get_option('cpp_std') != 'none' + refused_args += ['-std=' + get_option('cpp_std')] + endif + refused_args += pc_cflags + [ + '-I' + join_paths(meson.current_build_dir(), '..'), + '-I' + join_paths(meson.current_source_dir(), '..')] + test('refused_options', find_program('sh'), + args: [files('refused_options.sh')], + env: { + 'GAOL_CXX': ' '.join(cpp_comp.cmd_array()), + 'GAOL_CXXFLAGS': ' '.join(refused_args), + }, + suite: 'unit', timeout: 300) +endif + # The time per operation of GAOL and of the same operation on doubles, which # the continuous integration prints: not a test executable('gaol_performance', 'performance.cpp', diff --git a/tests/refused_options.cpp b/tests/refused_options.cpp index 9daed218..c636c0d2 100644 --- a/tests/refused_options.cpp +++ b/tests/refused_options.cpp @@ -6,10 +6,12 @@ * A program using GAOL, which the tests refused_finite_math_only and * refused_fast_math of tests/CMakeLists.txt compile with -ffinite-math-only * and with -ffast-math: the compilation has to fail with the message of - * gaol/gaol_config.h. With -ffinite-math-only, the compiler takes NaN and - * infinities never to occur, the empty interval, whose bounds are NaN, is no - * longer told empty (([1, 2] & [3, 4]).is_empty() is false), and the bounds - * computed are wrong. Compiled without them, this program is right: it + * gaol/gaol_config.h; refused_positive compiles it with neither, and the + * compilation has to succeed. tests/refused_options.sh does the same in the + * autotools and meson builds. With -ffinite-math-only, the compiler takes NaN + * and infinities never to occur, the empty interval, whose bounds are NaN, is + * no longer told empty (([1, 2] & [3, 4]).is_empty() is false), and the + * bounds computed are wrong. Compiled without them, this program is right: it * computes what the tests are about, and returns 0. * * Copyright (c) 2026 ENSTA, France diff --git a/tests/refused_options.sh b/tests/refused_options.sh new file mode 100644 index 00000000..a10b0b3f --- /dev/null +++ b/tests/refused_options.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# The options gaol/gaol_config.h refuses where GCC and Clang define a macro for +# them, in the autotools and meson builds (GAOL v5), as the tests +# refused_finite_math_only, refused_fast_math and refused_positive of +# tests/CMakeLists.txt check them in the CMake build: refused_options.cpp, a +# program using GAOL, is compiled with the compiler and the flags the build +# gives the code using GAOL ($GAOL_CXX and $GAOL_CXXFLAGS), followed by +# -ffinite-math-only, then by -ffast-math, and the compilation has to fail with +# the message of the refusal; with neither, it has to succeed. The option comes +# after the flags, whose -fno-fast-math turns it off otherwise. A compilation +# fails for many reasons, so its exit status only counts for the one that has +# to succeed. Only the preprocessor and the parser run (-fsyntax-only): the +# refusal is an #error. Not with Visual C++, which has neither option nor +# macro: neither build runs it there. As CMake runs its tests with GCC and Clang +# only, a compiler that does not define __GNUC__ skips it (exit status 77). + +: "${GAOL_CXX:?the compiler of the code using GAOL}" +source=$(dirname "$0")/refused_options.cpp +status=0 + +case $($GAOL_CXX $GAOL_CXXFLAGS -dM -E "$source" 2>/dev/null) in + *__GNUC__*) ;; + *) echo "SKIP: neither GCC nor Clang"; exit 77 ;; +esac + +if output=$($GAOL_CXX $GAOL_CXXFLAGS -fsyntax-only "$source" 2>&1); then + echo "PASS: compiled without a refused option" +else + printf '%s\n' "$output" + echo "FAIL: not compiled without a refused option" + status=1 +fi + +for option in -ffinite-math-only -ffast-math; do + output=$($GAOL_CXX $GAOL_CXXFLAGS $option -fsyntax-only "$source" 2>&1) + case $output in + *"GAOL cannot be compiled with $option"*) + echo "PASS: $option refused" ;; + *) + printf '%s\n' "$output" + echo "FAIL: $option not refused" + status=1 ;; + esac +done + +exit $status From 15cee3871b2a97f689226f8f06ba0f52779c8c03 Mon Sep 17 00:00:00 2001 From: Jordan08 Date: Tue, 6 Oct 2026 00:28:42 +0200 Subject: [PATCH 4/5] doc, manual, gaol_config.h: the -fno-fast-math sentences, is_empty() with -ffinite-math-only --- doc/three-builds.md | 16 +++++++++------- doc/using.md | 4 ++-- examples/examples.md | 7 ++++--- gaol/gaol_config.h | 24 +++++++++++++----------- manual/v5/gaol.tex | 3 ++- 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/doc/three-builds.md b/doc/three-builds.md index 6f338221..4db2127c 100644 --- a/doc/three-builds.md +++ b/doc/three-builds.md @@ -149,18 +149,20 @@ alone refuses, when compiling: | A compiler saying of `-frounding-math` "overriding currently unsupported rounding mode on this target", as Clang 14 for 64-bit ARM | Bounds of `pow()` and `nth_root()` did not enclose the exact values. Clang 18 honours the rounding direction there. | | MinGW-w64 whose `fma()` or `round()` is wrong: on x64, before mingw-w64 12 (GCC 11 to 13 of Chocolatey) and linked with `msvcrt.dll` rather than the UCRT (MSYS2 MINGW64, the cross compilers of Debian and Ubuntu); on 32-bit x86, before 11 (GCC 11 of WinLibs); on ARM, before 11 (not tested) | On x64, the `fma()` and `round()` of mingw-w64's own math library, computed in doubles: that `fma()` is not correctly rounded (10.7 % of the error-free products `fma(a, b, -a*b)` wrong), and CORE-MATH computes with it; that `round()` depends on the rounding direction. On 32-bit x86, the `fma()` of the mingw-w64 9 of WinLibs rounds each of its partial sums to a double. Bounds of `tan()`, `asin()`, `atan()` and others did not enclose the exact values. MinGW-w64 GCC 14 and 15 on x64, 12 to 15 on 32-bit x86, and MSYS2 UCRT64 and CLANG64 are built and tested. | | `-ffast-math`, `-Ofast`, `/fp:fast` | The compiler then rounds to nearest and drops the checks of NaN and infinities. Linking with them, or with `-funsafe-math-optimizations`, is a matter of its own (below). | -| `-ffinite-math-only`, which `-ffast-math` and `-Ofast` turn on | The compiler then takes NaN and infinities never to occur, in GAOL's inline functions too: the empty interval has NaN bounds, and `([1, 2] & [3, 4]).is_empty()` is false (GCC 9.4, Clang 18). | +| `-ffinite-math-only`, which `-ffast-math` and `-Ofast` turn on | The compiler then takes NaN and infinities never to occur, in GAOL's inline functions too: the empty interval has NaN bounds, and `([1, 2] & [3, 4]).is_empty()` is false (GCC 9.4 and 13 at `-O0`, `-O2` and `-O3`; Clang 18 at `-O0`, and at `-O2` and `-O3` when the bounds are `volatile`). | | Visual C++, and clang-cl from Clang 16, without `/fp:strict` (`/fp:precise`, their default) | They then assume rounding to nearest, and may evaluate or rewrite floating-point operations accordingly (clang-cl compiles with `-fno-rounding-math -ffp-contract=on`; GAOL v5 did not refuse it at first): no test gave a wrong bound so, but nothing certifies the bounds (see [What differs from GAOL](differences.md)). | | Doubles computed on the x87 unit of 32-bit x86 processors (without `-msse2 -mfpmath=sse`, or `/arch:SSE2`) | CORE-MATH assumes every operation on doubles rounded to a double. Computed in extended precision, its results rounded to nearest are rounded twice, and 175 arguments gave the other neighbour of the exact value with GCC 12 on Debian 12 i386; with GCC 9, which rounds to nearest at compile time the constants CORE-MATH rounds in the direction in effect, GAOL's bounds of `exp2(-1075)`, `expm1(-800)` or `atan2()` of a tiny and a huge number did not enclose the exact values. `tests/extended_precision.cpp` checks these arguments. | `-fno-fast-math`, one of the flags of interval arithmetic, turns `-ffast-math` and `-ffinite-math-only` off when it comes after them on the command line, and -not when `-ffast-math` or `-ffinite-math-only` comes before it: `gaol.pc` and `gaol::gaol` give it, and the code -including GAOL's headers is refused when `-ffast-math` or `-ffinite-math-only` follows it. The CMake tests -`refused_finite_math_only` and `refused_fast_math` compile -`tests/refused_options.cpp` with each of the two options, after the flags of -interval arithmetic, and check that `gaol/gaol_config.h` refuses it (GCC and -Clang). +not when it comes before them: `gaol.pc` and `gaol::gaol` give it, and the code +including GAOL's headers is refused when `-ffast-math` or `-ffinite-math-only` +comes after `-fno-fast-math`. The CMake tests `refused_finite_math_only` and +`refused_fast_math` compile `tests/refused_options.cpp` with each of the two +options, after the flags of interval arithmetic, and check that +`gaol/gaol_config.h` refuses it, and `refused_positive` that it compiles +without them (GCC and Clang); `tests/refused_options.sh` does the same in the +autotools and meson builds. No macro of the compiler shows the following, which `gaol/gaol_config.h` cannot refuse and which give wrong results all the same: the code using GAOL diff --git a/doc/using.md b/doc/using.md index b7bfc96c..ca42c65e 100644 --- a/doc/using.md +++ b/doc/using.md @@ -31,8 +31,8 @@ what contradicts them, `-ffast-math`, `-ffinite-math-only` and doubles computed on the x87 unit (see [Compilers and options refused](three-builds.md#compilers-and-options-refused)). `-fno-fast-math` turns the first two off when it comes after them on the -command line, and does nothing when `-ffast-math` or `-ffinite-math-only` comes before it, where the -compilation stops. +command line, and does nothing when `-ffast-math` or `-ffinite-math-only` comes +after `-fno-fast-math`, where the compilation stops. With `-frounding-math` on x86, GCC 12.1 to 12.3 and 13.1 to 13.2 (Debian 12 has GCC 12.2) initialize wrongly a double member of a static array of diff --git a/examples/examples.md b/examples/examples.md index ae7590e7..34199912 100644 --- a/examples/examples.md +++ b/examples/examples.md @@ -1072,9 +1072,10 @@ check: #endif ``` -and a row in the refused options of `doc/three-builds.md`. Test: a -`try_compile()` at configure time, as `tests/fp_strict` does for other -options (Visual C++ only). +and a row in the refused options of `doc/three-builds.md`. Test: a compile +test with `PASS_REGULAR_EXPRESSION "ffinite-math-only"` (`tests/fp_strict`, +the check of Visual C++ and clang-cl without `/fp:strict`, is a +`try_compile()` at configure time instead). **5. `atanh([1, x])`.** In `atanh()`, return the empty set when `J.left() == 1.0 || J.right() == -1.0` (J being `I & [-1, 1]`), and write the diff --git a/gaol/gaol_config.h b/gaol/gaol_config.h index a2ad5ec9..5dce87c9 100644 --- a/gaol/gaol_config.h +++ b/gaol/gaol_config.h @@ -196,17 +196,19 @@ /* -ffinite-math-only, which -ffast-math and -Ofast turn on, has the compiler take NaN and infinities never to occur, in the inline functions of GAOL's headers as anywhere else: the empty interval has NaN bounds, and is_empty() - reads it as !std::islessequal(left(), right()). The compiler then folds the test away, and - ([1, 2] & [3, 4]).is_empty() is false with -ffinite-math-only: with GCC 13 at -O0, -O2 and -O3, - and with Clang 18 at -O0, and at -O2 and -O3 only with volatile bounds: the tests - refused_finite_math_only and refused_fast_math (tests/CMakeLists.txt) check - the refusal. GCC and Clang define __FINITE_MATH_ONLY__, as 0 or 1; Visual - C++ defines nothing of the kind (its /fp:fast is refused below). This also - refuses what __FAST_MATH__ does not show: with Clang, -Ofast or -ffast-math - followed by -frounding-math leave it undefined, and __FINITE_MATH_ONLY__ at - 1. -fno-fast-math, one of the flags of gaol.pc and gaol::gaol, turns the - option off when it comes after it on the command line, and does nothing when - it comes before. + reads it as !std::islessequal(left(), right()) (std::isunordered() first on + 32-bit ARM and POWER9). The compiler then folds the test away, and + ([1, 2] & [3, 4]).is_empty() is false: with GCC 9.4 and 13 at -O0, -O2 and + -O3, with Clang 18 at -O0, and at -O2 and -O3 when the bounds are volatile. + The tests refused_finite_math_only and refused_fast_math + (tests/CMakeLists.txt) and tests/refused_options.sh check the refusal. GCC + and Clang define __FINITE_MATH_ONLY__, as 0 or 1; Visual C++ defines nothing + of the kind (its /fp:fast is refused below). This also refuses what + __FAST_MATH__ does not show: with Clang, -Ofast or -ffast-math followed by + -frounding-math leave it undefined, and __FINITE_MATH_ONLY__ at 1. + -fno-fast-math, one of the flags of gaol.pc and gaol::gaol, turns the option + off when the option comes before it on the command line, and does nothing + when the option comes after it, where the compilation stops here. No macro shows what follows, which GAOL cannot refuse: -funsafe-math-optimizations and -ffast-math -fno-finite-math-only, with diff --git a/manual/v5/gaol.tex b/manual/v5/gaol.tex index e20aa740..4952635e 100644 --- a/manual/v5/gaol.tex +++ b/manual/v5/gaol.tex @@ -362,7 +362,8 @@ \subsection{Compilers and options refused}\label{sec:refused} \noindent \option{-fno-fast-math}, one of the flags of interval arithmetic, turns \option{-ffast-math} and \option{-ffinite-math-only} off when it comes after -them on the command line, and does nothing when it comes before them, where the +them on the command line, and does nothing when \option{-ffast-math} or +\option{-ffinite-math-only} comes after \option{-fno-fast-math}, where the compilation stops. No macro of the compiler shows the following, which \file{gaol/gaol\_config.h} cannot refuse and which give wrong results all the same: \option{-funsafe-math-optimizations} and \option{-ffast-math From 01e6df6c609ef6ec1753deaaf2d9f614c8369ddb Mon Sep 17 00:00:00 2001 From: Jordan08 Date: Tue, 6 Oct 2026 07:57:19 +0200 Subject: [PATCH 5/5] gaol_config.h: a remedy in both #error, -Ofast under -fno-fast-math documented, volatile bounds --- doc/three-builds.md | 8 ++++++-- doc/using.md | 4 +++- gaol/gaol_config.h | 4 ++-- manual/v5/gaol.tex | 10 ++++++++-- tests/refused_options.cpp | 10 +++++++--- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/doc/three-builds.md b/doc/three-builds.md index 4db2127c..fe6348da 100644 --- a/doc/three-builds.md +++ b/doc/three-builds.md @@ -148,7 +148,7 @@ alone refuses, when compiling: | Clang for 32-bit ARM processors | It does not honour the rounding direction there: built by Clang 21, 4556 of 16000 random products, squares and cubes did not enclose their exact values. GCC does. | | A compiler saying of `-frounding-math` "overriding currently unsupported rounding mode on this target", as Clang 14 for 64-bit ARM | Bounds of `pow()` and `nth_root()` did not enclose the exact values. Clang 18 honours the rounding direction there. | | MinGW-w64 whose `fma()` or `round()` is wrong: on x64, before mingw-w64 12 (GCC 11 to 13 of Chocolatey) and linked with `msvcrt.dll` rather than the UCRT (MSYS2 MINGW64, the cross compilers of Debian and Ubuntu); on 32-bit x86, before 11 (GCC 11 of WinLibs); on ARM, before 11 (not tested) | On x64, the `fma()` and `round()` of mingw-w64's own math library, computed in doubles: that `fma()` is not correctly rounded (10.7 % of the error-free products `fma(a, b, -a*b)` wrong), and CORE-MATH computes with it; that `round()` depends on the rounding direction. On 32-bit x86, the `fma()` of the mingw-w64 9 of WinLibs rounds each of its partial sums to a double. Bounds of `tan()`, `asin()`, `atan()` and others did not enclose the exact values. MinGW-w64 GCC 14 and 15 on x64, 12 to 15 on 32-bit x86, and MSYS2 UCRT64 and CLANG64 are built and tested. | -| `-ffast-math`, `-Ofast`, `/fp:fast` | The compiler then rounds to nearest and drops the checks of NaN and infinities. Linking with them, or with `-funsafe-math-optimizations`, is a matter of its own (below). | +| `-ffast-math`, `-Ofast`, `/fp:fast` | The compiler then rounds to nearest and drops the checks of NaN and infinities. `-Ofast` is not refused where `-fno-fast-math` turns its fast-math optimizations off (below). Linking with them, or with `-funsafe-math-optimizations`, is a matter of its own (below). | | `-ffinite-math-only`, which `-ffast-math` and `-Ofast` turn on | The compiler then takes NaN and infinities never to occur, in GAOL's inline functions too: the empty interval has NaN bounds, and `([1, 2] & [3, 4]).is_empty()` is false (GCC 9.4 and 13 at `-O0`, `-O2` and `-O3`; Clang 18 at `-O0`, and at `-O2` and `-O3` when the bounds are `volatile`). | | Visual C++, and clang-cl from Clang 16, without `/fp:strict` (`/fp:precise`, their default) | They then assume rounding to nearest, and may evaluate or rewrite floating-point operations accordingly (clang-cl compiles with `-fno-rounding-math -ffp-contract=on`; GAOL v5 did not refuse it at first): no test gave a wrong bound so, but nothing certifies the bounds (see [What differs from GAOL](differences.md)). | | Doubles computed on the x87 unit of 32-bit x86 processors (without `-msse2 -mfpmath=sse`, or `/arch:SSE2`) | CORE-MATH assumes every operation on doubles rounded to a double. Computed in extended precision, its results rounded to nearest are rounded twice, and 175 arguments gave the other neighbour of the exact value with GCC 12 on Debian 12 i386; with GCC 9, which rounds to nearest at compile time the constants CORE-MATH rounds in the direction in effect, GAOL's bounds of `exp2(-1075)`, `expm1(-800)` or `atan2()` of a tiny and a huge number did not enclose the exact values. `tests/extended_precision.cpp` checks these arguments. | @@ -162,7 +162,11 @@ comes after `-fno-fast-math`. The CMake tests `refused_finite_math_only` and options, after the flags of interval arithmetic, and check that `gaol/gaol_config.h` refuses it, and `refused_positive` that it compiles without them (GCC and Clang); `tests/refused_options.sh` does the same in the -autotools and meson builds. +autotools and meson builds. With GCC, `-fno-fast-math` turns the fast-math +optimizations of `-Ofast` off wherever it stands on the command line, before +`-Ofast` too, and the header has nothing to refuse: of them, GCC 9.4 keeps +only `-fcx-limited-range`, which changes the arithmetic of complex numbers +alone. Clang turns them off only when `-fno-fast-math` comes after `-Ofast`. No macro of the compiler shows the following, which `gaol/gaol_config.h` cannot refuse and which give wrong results all the same: the code using GAOL diff --git a/doc/using.md b/doc/using.md index ca42c65e..8a14fbfd 100644 --- a/doc/using.md +++ b/doc/using.md @@ -32,7 +32,9 @@ on the x87 unit (see [Compilers and options refused](three-builds.md#compilers-and-options-refused)). `-fno-fast-math` turns the first two off when it comes after them on the command line, and does nothing when `-ffast-math` or `-ffinite-math-only` comes -after `-fno-fast-math`, where the compilation stops. +after `-fno-fast-math`, where the compilation stops. With GCC, it turns the +fast-math optimizations of `-Ofast` off wherever it stands, and `-Ofast` is not +refused there. With `-frounding-math` on x86, GCC 12.1 to 12.3 and 13.1 to 13.2 (Debian 12 has GCC 12.2) initialize wrongly a double member of a static array of diff --git a/gaol/gaol_config.h b/gaol/gaol_config.h index 5dce87c9..1bae846b 100644 --- a/gaol/gaol_config.h +++ b/gaol/gaol_config.h @@ -191,7 +191,7 @@ --------------------------------------------------------------------------- */ #if defined(__FAST_MATH__) -# error "GAOL cannot be compiled with -ffast-math (nor -Ofast): the bounds it computes would not enclose the exact results" +# error "GAOL cannot be compiled with -ffast-math (nor -Ofast): the bounds it computes would not enclose the exact results (remove the option, or put -fno-fast-math, one of the flags of gaol.pc and gaol::gaol, after it)" #endif /* -ffinite-math-only, which -ffast-math and -Ofast turn on, has the compiler take NaN and infinities never to occur, in the inline functions of GAOL's @@ -221,7 +221,7 @@ -fno-honor-nans of Clang, given without -fno-honor-infinities, which does to the empty interval what -ffinite-math-only does. */ #if defined(__FINITE_MATH_ONLY__) && __FINITE_MATH_ONLY__ -# error "GAOL cannot be compiled with -ffinite-math-only (which -ffast-math and -Ofast turn on): its empty interval has NaN bounds and its unbounded ones infinite bounds, which the compiler then takes never to occur (-fno-fast-math, one of the flags of gaol.pc and gaol::gaol, turns it off when it comes after)" +# error "GAOL cannot be compiled with -ffinite-math-only (which -ffast-math and -Ofast turn on): its empty interval has NaN bounds and its unbounded ones infinite bounds, which the compiler then takes never to occur (remove the option, or put -fno-fast-math, one of the flags of gaol.pc and gaol::gaol, after the option that turns it on)" #endif #if defined(_M_FP_FAST) # error "GAOL cannot be compiled with /fp:fast: the bounds it computes would not enclose the exact results (it needs /fp:strict)" diff --git a/manual/v5/gaol.tex b/manual/v5/gaol.tex index 4952635e..75555703 100644 --- a/manual/v5/gaol.tex +++ b/manual/v5/gaol.tex @@ -364,7 +364,11 @@ \subsection{Compilers and options refused}\label{sec:refused} \option{-ffast-math} and \option{-ffinite-math-only} off when it comes after them on the command line, and does nothing when \option{-ffast-math} or \option{-ffinite-math-only} comes after \option{-fno-fast-math}, where the -compilation stops. No macro of the compiler shows the following, which +compilation stops. With GCC, it turns the fast-math optimizations of +\option{-Ofast} off wherever it stands on the command line, before +\option{-Ofast} too, and \option{-Ofast} is not refused there: of them, GCC~9.4 +keeps only \option{-fcx-limited-range}, which changes the arithmetic of complex +numbers alone. No macro of the compiler shows the following, which \file{gaol/gaol\_config.h} cannot refuse and which give wrong results all the same: \option{-funsafe-math-optimizations} and \option{-ffast-math -fno-finite-math-only} with GCC, which rewrites the addition by which GAOL sees @@ -1495,7 +1499,9 @@ \subsection{The flags of interval arithmetic} % c++ -ffast-math -c program.cpp gaol/gaol_config.h: error: #error "GAOL cannot be compiled with -ffast-math (nor -Ofast): the bounds it -computes would not enclose the exact results" +computes would not enclose the exact results (remove +the option, or put -fno-fast-math, one of the flags of +gaol.pc and gaol::gaol, after it)" \end{onscreen} \noindent diff --git a/tests/refused_options.cpp b/tests/refused_options.cpp index c636c0d2..a18adb04 100644 --- a/tests/refused_options.cpp +++ b/tests/refused_options.cpp @@ -11,8 +11,11 @@ * autotools and meson builds. With -ffinite-math-only, the compiler takes NaN * and infinities never to occur, the empty interval, whose bounds are NaN, is * no longer told empty (([1, 2] & [3, 4]).is_empty() is false), and the - * bounds computed are wrong. Compiled without them, this program is right: it - * computes what the tests are about, and returns 0. + * bounds computed are wrong. The bounds are read from volatile doubles: with + * constants, Clang 18 computed the intersection at compile time at -O2 and + * -O3, and the program told it empty with -ffinite-math-only. Compiled without + * the refused options, this program is right: it computes what the tests are + * about, and returns 0. * * Copyright (c) 2026 ENSTA, France * @@ -26,7 +29,8 @@ int main() { - const gaol::interval a(1.0, 2.0), b(3.0, 4.0); + volatile double one = 1.0, two = 2.0, three = 3.0, four = 4.0; + const gaol::interval a(one, two), b(three, four); const bool empty = (a & b).is_empty(); gaol::cleanup(); return empty ? 0 : 1;