From b3937f09b6fbfcf35a9587b62c656e29d6442b90 Mon Sep 17 00:00:00 2001 From: Ian Pike Date: Sat, 13 Jun 2026 15:55:14 -0400 Subject: [PATCH 01/59] CCMath v0.3.0 (#123) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../actions/install-clang-format/action.yml | 48 + .github/workflows/ci-integration.yml | 85 + .github/workflows/ci-linux.yml | 207 +- .github/workflows/ci-macos.yml | 177 +- .github/workflows/ci-windows.yml | 178 +- .github/workflows/codeql.yml | 10 +- .github/workflows/dependency-review.yml | 27 - .github/workflows/fuzz.yml | 112 +- .github/workflows/lint.yml | 82 +- .github/workflows/scorecard.yml | 10 +- .gitignore | 50 + CMakeLists.txt | 25 +- CMakePresets.json | 489 ++- CONTRIBUTING.md | 267 +- README.md | 400 +- SECURITY.md | 67 +- benchmarks/CMakeLists.txt | 33 +- benchmarks/README.md | 38 + benchmarks/benchmarks/basic/abs.bench.cpp | 28 - benchmarks/benchmarks/basic/abs.bench.hpp | 78 - benchmarks/benchmarks/basic/fdim.bench.cpp | 27 - benchmarks/benchmarks/basic/fdim.bench.hpp | 80 - benchmarks/benchmarks/basic/fma.bench.cpp | 27 - benchmarks/benchmarks/basic/fma.bench.hpp | 84 - benchmarks/benchmarks/nearest/trunc.bench.hpp | 0 benchmarks/benchmarks/power/sqrt.bench.cpp | 50 - benchmarks/benchmarks/power/sqrt.bench.hpp | 149 - benchmarks/ccmath_benchmark_main.cpp | 284 -- benchmarks/helpers/randomizers.hpp | 46 - benchmarks/shared/harness.hpp | 104 + benchmarks/shared/inputs.hpp | 51 + benchmarks/shared/paths.hpp | 35 + benchmarks/shared/register.hpp | 209 + benchmarks/src/math/basic/abs.bench.cpp | 9 + benchmarks/src/math/basic/fdim.bench.cpp | 9 + benchmarks/src/math/basic/fma.bench.cpp | 9 + benchmarks/src/math/nearest/trunc.bench.cpp | 9 + benchmarks/src/math/power/sqrt.bench.cpp | 23 + benchmarks/tools/plot.py | 165 - cmake/README.md | 41 + cmake/config/BenchmarkModuleRegistry.cmake | 16 + cmake/config/BuildManifest.cmake | 114 + cmake/config/EmitBuildManifest.cmake | 148 + cmake/config/ExampleModuleRegistry.cmake | 12 + cmake/config/FindCoreMath.cmake | 84 + cmake/config/FindMpfrGmp.cmake | 62 + cmake/config/FuzzModuleRegistry.cmake | 13 + cmake/config/OracleCampaignRegistry.cmake | 159 + cmake/config/ProjectCompileDefinitions.cmake | 18 +- cmake/config/ShowcaseDeps.cmake | 73 + cmake/config/TestCtestTargets.cmake | 57 + cmake/config/TestModuleRegistry.cmake | 26 + cmake/config/TestOracleTargets.cmake | 134 + cmake/config/TestValidationOptions.cmake | 55 + cmake/config/UserOptions.cmake | 79 +- cmake/coremath/CMakeLists.txt | 45 + cmake/coremath/crmath.h | 13 + .../CheckAllSupportedSimdFeatures.cmake | 4 + .../simd/SelectSimdInstructionSet.cmake | 133 + cmake/gen/meson.build | 71 + cmake/helpers/CcmBenchHelpers.cmake | 86 + cmake/helpers/CcmExampleHelpers.cmake | 56 + cmake/helpers/CcmFuzzHelpers.cmake | 129 + cmake/helpers/CcmOptionalTooling.cmake | 82 + cmake/helpers/CcmOracleHelpers.cmake | 115 + cmake/helpers/CcmTestHelpers.cmake | 283 ++ cmake/modules/CcmLibFuzzer.cmake | 16 +- cmake/vcpkg/vcpkg.json | 10 + docs/ROUNDING_STATUS.md | 91 + docs/STATUS.md | 201 +- docs/ULP_STATUS.md | 892 ++++ .../ACM-Reference-Format.bst | 3224 ++++++++++++++ .../APPROXIMATING_FUNCTIONS.pdf | Bin 0 -> 728763 bytes .../APPROXIMATING_FUNCTIONS.tex | 3712 +++++++++++++++++ docs/approximating_functions/README.md | 26 + .../approx_functions.bib | 269 ++ docs/approximating_functions/build.sh | 152 + .../cpp/directed_rounding_demo.cpp | 41 + .../cpp/sin_small_check.cpp | 87 + .../gappa/sin_small_eval.gappa | 34 + .../sollya/exp_small_fpminimax.sollya | 25 + .../sollya/hard_case_scan.sollya | 36 + .../sollya/log2_core_points.sollya | 48 + .../sollya/log2_core_poly.sollya | 50 + .../sollya/log2_core_table.sollya | 64 + .../sollya/sin_minimax_compare.sollya | 34 + .../sollya/sin_small_error.sollya | 73 + .../sollya/sin_small_fpminimax.sollya | 33 + .../sollya/sin_taylor_degree_error.sollya | 52 + .../sollya/sin_taylor_error.sollya | 30 + docs/proofs/power/POW_PROOF.tex | 1162 ++++++ examples/CMakeLists.txt | 14 +- examples/README.md | 54 + examples/common/io.hpp | 36 + .../compiler_support.md} | 0 examples/item/README.md | 22 + examples/item/constexpr_abs.run.xml | 7 + examples/item/constexpr_smoke.run.xml | 7 + examples/item/decibel_gain.run.xml | 7 + examples/item/fp_classes.run.xml | 7 + examples/item/hypot_distance.run.xml | 7 + examples/item/rounding_edge_cases.run.xml | 7 + examples/item/signed_magnitude.run.xml | 7 + examples/item/unit_circle.run.xml | 7 + examples/main.cpp | 73 - examples/src/constexpr_abs.cpp | 33 + examples/src/constexpr_smoke.cpp | 16 + examples/src/decibel_gain.cpp | 35 + examples/src/fp_classes.cpp | 59 + examples/src/hypot_distance.cpp | 44 + examples/src/rounding_edge_cases.cpp | 41 + examples/src/signed_magnitude.cpp | 45 + examples/src/unit_circle.cpp | 50 + fuzzing/CMakeLists.txt | 79 +- fuzzing/README.md | 31 + fuzzing/basic_fuzz.cpp | 45 - fuzzing/{ => cmake}/run_fuzz_campaign.cmake | 24 +- fuzzing/cmake/run_fuzz_smoke.cmake | 44 + fuzzing/compare_fuzz.cpp | 48 - fuzzing/dicts/ccmath.dict | 12 + fuzzing/expo_fuzz.cpp | 41 - fuzzing/fmanip_fuzz.cpp | 48 - fuzzing/generate_fuzz_seeds.cmake | 43 - fuzzing/nearest_fuzz.cpp | 40 - fuzzing/power_fuzz.cpp | 39 - fuzzing/run_fuzz_smoke.cmake | 31 - fuzzing/scripts/generate_seeds.py | 65 + fuzzing/shared/entry.hpp | 37 + fuzzing/shared/fuzz_calls.hpp | 136 + fuzzing/{ => shared}/harness.hpp | 66 +- fuzzing/shared/input.hpp | 179 + fuzzing/src/math/basic_fuzz.cpp | 37 + fuzzing/src/math/compare_fuzz.cpp | 42 + fuzzing/src/math/expo_fuzz.cpp | 53 + fuzzing/src/math/fmanip_fuzz.cpp | 41 + fuzzing/{ => src/math}/misc_fuzz.cpp | 27 +- fuzzing/src/math/nearest_fuzz.cpp | 35 + fuzzing/src/math/power_fuzz.cpp | 64 + fuzzing/src/math/trig_fuzz.cpp | 46 + fuzzing/trig_fuzz.cpp | 42 - include/ccmath/ext/CMakeLists.txt | 15 + include/ccmath/ext/README.md | 11 +- include/ccmath/ext/abs_diff.hpp | 27 + include/ccmath/ext/align.hpp | 17 +- include/ccmath/ext/approximately.hpp | 30 + include/ccmath/ext/ceil_div.hpp | 53 + include/ccmath/ext/chgsign.hpp | 39 + include/ccmath/ext/clamp.hpp | 8 +- include/ccmath/ext/degrees.hpp | 4 +- include/ccmath/ext/delta_angle.hpp | 55 + include/ccmath/ext/fract.hpp | 4 +- include/ccmath/ext/inverse_lerp.hpp | 54 + include/ccmath/ext/is_power_of_two.hpp | 77 +- include/ccmath/ext/lerp_angle.hpp | 51 + include/ccmath/ext/lerp_smooth.hpp | 2 - include/ccmath/ext/mix.hpp | 4 +- include/ccmath/ext/move_towards.hpp | 36 + include/ccmath/ext/move_towards_angle.hpp | 52 + include/ccmath/ext/normalize.hpp | 33 +- include/ccmath/ext/radians.hpp | 4 +- include/ccmath/ext/rcp.hpp | 4 +- include/ccmath/ext/remap.hpp | 74 + include/ccmath/ext/repeat.hpp | 51 + include/ccmath/ext/saturate.hpp | 28 + include/ccmath/ext/sign.hpp | 26 + include/ccmath/ext/step.hpp | 27 + include/ccmath/ext/unlerp.hpp | 60 + include/ccmath/internal/README.md | 16 +- .../config/arch/check_simd_support.hpp | 2 +- .../internal/config/builtin/CMakeLists.txt | 2 +- .../config/builtin/copysign_support.hpp | 1 - include/ccmath/internal/config/compiler.hpp | 22 +- .../internal/config/long_double_format.hpp | 51 + .../ccmath/internal/config/powl_policy.hpp | 28 + .../ccmath/internal/config/type_support.hpp | 2 +- include/ccmath/internal/math/README.md | 4 + .../internal/math/common/basic/fabs.hpp | 4 +- .../internal/math/common/basic/fdim.hpp | 4 +- .../ccmath/internal/math/common/basic/fma.hpp | 70 +- .../internal/math/common/basic/fmod.hpp | 24 +- .../ccmath/internal/math/common/basic/max.hpp | 23 +- .../ccmath/internal/math/common/basic/min.hpp | 15 +- .../ccmath/internal/math/common/basic/nan.hpp | 8 +- .../internal/math/common/basic/remainder.hpp | 12 +- .../internal/math/common/basic/remquo.hpp | 9 +- .../internal/math/common/compare/isfinite.hpp | 2 +- .../math/common/compare/isgreater.hpp | 7 +- .../math/common/compare/isgreaterequal.hpp | 7 +- .../internal/math/common/compare/isinf.hpp | 2 +- .../internal/math/common/compare/isless.hpp | 7 +- .../math/common/compare/islessequal.hpp | 7 +- .../math/common/compare/islessgreater.hpp | 7 +- .../internal/math/common/compare/isnan.hpp | 3 +- .../internal/math/common/compare/isnormal.hpp | 11 +- .../math/common/compare/isunordered.hpp | 7 +- .../internal/math/common/compare/signbit.hpp | 3 +- .../ccmath/internal/math/common/expo/exp.hpp | 22 +- .../ccmath/internal/math/common/expo/exp2.hpp | 23 +- .../internal/math/common/expo/expm1.hpp | 21 +- .../ccmath/internal/math/common/expo/log.hpp | 22 +- .../internal/math/common/expo/log10.hpp | 21 +- .../internal/math/common/expo/log1p.hpp | 21 +- .../ccmath/internal/math/common/expo/log2.hpp | 24 +- .../internal/math/common/fmanip/copysign.hpp | 14 +- .../internal/math/common/fmanip/frexp.hpp | 19 +- .../internal/math/common/fmanip/ldexp.hpp | 39 +- .../internal/math/common/fmanip/modf.hpp | 10 +- .../internal/math/common/fmanip/nextafter.hpp | 19 +- .../math/common/fmanip/nexttoward.hpp | 20 +- .../internal/math/common/fmanip/scalbn.hpp | 28 +- .../ccmath/internal/math/common/misc/lerp.hpp | 2 - .../internal/math/common/nearest/floor.hpp | 33 +- .../math/common/nearest/nearbyint.hpp | 12 +- .../internal/math/common/nearest/trunc.hpp | 14 +- .../ccmath/internal/math/common/power/pow.hpp | 2 +- .../internal/math/common/power/sqrt.hpp | 14 +- .../ccmath/internal/math/common/trig/cos.hpp | 14 +- .../ccmath/internal/math/common/trig/sin.hpp | 14 +- .../ccmath/internal/math/common/trig/tan.hpp | 14 +- .../math/generic/builtins/basic/fabs.hpp | 49 +- .../math/generic/builtins/basic/fdim.hpp | 43 +- .../math/generic/builtins/basic/fma.hpp | 86 +- .../math/generic/builtins/basic/fmax.hpp | 43 +- .../math/generic/builtins/basic/fmin.hpp | 43 +- .../math/generic/builtins/basic/fmod.hpp | 27 +- .../math/generic/builtins/basic/nan.hpp | 43 +- .../math/generic/builtins/basic/remainder.hpp | 27 +- .../math/generic/builtins/basic/remquo.hpp | 43 +- .../math/generic/builtins/builtin_helpers.hpp | 17 + .../generic/builtins/compare/isfinite.hpp | 35 +- .../generic/builtins/compare/isgreater.hpp | 35 +- .../builtins/compare/isgreaterequal.hpp | 35 +- .../math/generic/builtins/compare/isinf.hpp | 35 +- .../math/generic/builtins/compare/isless.hpp | 35 +- .../generic/builtins/compare/islessequal.hpp | 35 +- .../builtins/compare/islessgreater.hpp | 35 +- .../math/generic/builtins/compare/isnan.hpp | 37 +- .../generic/builtins/compare/isnormal.hpp | 35 +- .../generic/builtins/compare/isunordered.hpp | 35 +- .../math/generic/builtins/compare/signbit.hpp | 35 +- .../math/generic/builtins/expo/exp.hpp | 31 +- .../math/generic/builtins/expo/exp2.hpp | 31 +- .../math/generic/builtins/expo/expm1.hpp | 31 +- .../math/generic/builtins/expo/log.hpp | 45 +- .../math/generic/builtins/expo/log10.hpp | 45 +- .../math/generic/builtins/expo/log1p.hpp | 45 +- .../math/generic/builtins/expo/log2.hpp | 45 +- .../math/generic/builtins/fmanip/copysign.hpp | 43 +- .../math/generic/builtins/fmanip/frexp.hpp | 43 +- .../math/generic/builtins/fmanip/ilogb.hpp | 43 +- .../math/generic/builtins/fmanip/ldexp.hpp | 43 +- .../math/generic/builtins/fmanip/logb.hpp | 43 +- .../math/generic/builtins/fmanip/modf.hpp | 43 +- .../generic/builtins/fmanip/nextafter.hpp | 43 +- .../generic/builtins/fmanip/nexttoward.hpp | 43 +- .../math/generic/builtins/fmanip/scalbn.hpp | 43 +- .../math/generic/builtins/hyper/acosh.hpp | 45 +- .../math/generic/builtins/hyper/asinh.hpp | 45 +- .../math/generic/builtins/hyper/atanh.hpp | 45 +- .../math/generic/builtins/hyper/cosh.hpp | 45 +- .../math/generic/builtins/hyper/sinh.hpp | 45 +- .../math/generic/builtins/hyper/tanh.hpp | 45 +- .../math/generic/builtins/misc/erf.hpp | 43 +- .../math/generic/builtins/misc/erfc.hpp | 43 +- .../math/generic/builtins/misc/lgamma.hpp | 27 +- .../math/generic/builtins/misc/tgamma.hpp | 33 +- .../math/generic/builtins/nearest/ceil.hpp | 43 +- .../math/generic/builtins/nearest/floor.hpp | 43 +- .../generic/builtins/nearest/nearbyint.hpp | 107 + .../math/generic/builtins/nearest/rint.hpp | 107 + .../math/generic/builtins/nearest/round.hpp | 85 +- .../math/generic/builtins/nearest/trunc.hpp | 43 +- .../math/generic/builtins/power/cbrt.hpp | 44 +- .../math/generic/builtins/power/hypot.hpp | 44 +- .../math/generic/builtins/power/pow.hpp | 46 +- .../math/generic/builtins/power/sqrt.hpp | 41 +- .../math/generic/builtins/trig/acos.hpp | 45 +- .../math/generic/builtins/trig/asin.hpp | 45 +- .../math/generic/builtins/trig/atan.hpp | 45 +- .../math/generic/builtins/trig/atan2.hpp | 43 +- .../math/generic/builtins/trig/cos.hpp | 45 +- .../math/generic/builtins/trig/sin.hpp | 43 +- .../math/generic/builtins/trig/tan.hpp | 45 +- .../math/generic/func/basic/fdim_gen.hpp | 2 +- .../math/generic/func/basic/fma_gen.hpp | 140 +- .../math/generic/func/basic/fmod_gen.hpp | 24 +- .../math/generic/func/basic/max_gen.hpp | 4 + .../math/generic/func/basic/min_gen.hpp | 4 + .../math/generic/func/basic/remainder_gen.hpp | 12 +- .../basic/remquo_impl/remquo_double_impl.hpp | 4 +- .../basic/remquo_impl/remquo_float_impl.hpp | 4 +- .../basic/remquo_impl/remquo_ldouble_impl.hpp | 4 +- .../math/generic/func/compare/isfinite.hpp | 2 +- .../math/generic/func/compare/isgreater.hpp | 7 +- .../generic/func/compare/isgreaterequal.hpp | 7 +- .../math/generic/func/compare/isinf.hpp | 2 +- .../math/generic/func/compare/isless.hpp | 7 +- .../math/generic/func/compare/islessequal.hpp | 7 +- .../generic/func/compare/islessgreater.hpp | 7 +- .../math/generic/func/compare/isnan.hpp | 3 +- .../math/generic/func/compare/isnormal.hpp | 11 +- .../math/generic/func/compare/isunordered.hpp | 7 +- .../math/generic/func/compare/signbit.hpp | 3 +- .../math/generic/func/expo/exp2_gen.hpp | 125 +- .../func/expo/exp2_impl/CMakeLists.txt | 4 - .../generic/func/expo/exp2_impl/exp2_impl.hpp | 31 - .../func/expo/exp2_impl/exp2f_impl.hpp | 31 - .../math/generic/func/expo/exp_gen.hpp | 31 +- .../math/generic/func/expo/expm1_gen.hpp | 34 +- .../math/generic/func/expo/log10_gen.hpp | 8 +- .../math/generic/func/expo/log1p_gen.hpp | 43 +- .../math/generic/func/expo/log2_gen.hpp | 31 +- .../math/generic/func/expo/log_gen.hpp | 50 +- .../math/generic/func/fmanip/CMakeLists.txt | 2 + .../math/generic/func/fmanip/frexp_gen.hpp | 11 +- .../math/generic/func/fmanip/ilogb_gen.hpp | 19 +- .../generic/func/fmanip/impl/CMakeLists.txt | 5 + .../generic/func}/fmanip/impl/frexp_impl.hpp | 0 .../generic/func}/fmanip/impl/ilogb_impl.hpp | 2 +- .../generic/func}/fmanip/impl/logb_impl.hpp | 2 +- .../math/generic/func/fmanip/ldexp_gen.hpp | 13 +- .../math/generic/func/fmanip/logb_gen.hpp | 19 +- .../math/generic/func/fmanip/modf_gen.hpp | 14 +- .../generic/func/fmanip/nextafter_gen.hpp | 30 +- .../math/generic/func/fmanip/scalbn_gen.hpp | 24 +- .../math/generic/func/misc/lerp_gen.hpp | 4 +- .../math/generic/func/nearest/floor_gen.hpp | 29 +- .../math/generic/func/nearest/trunc_gen.hpp | 12 +- .../math/generic/func/power/CMakeLists.txt | 2 + .../math/generic/func/power/cbrt_gen.hpp | 19 +- .../math/generic/func/power/hypot_gen.hpp | 19 +- .../generic/func}/power/impl/CMakeLists.txt | 0 .../generic/func}/power/impl/cbrt_impl.hpp | 0 .../generic/func}/power/impl/hypot_impl.hpp | 0 .../math/generic/func/power/pow_gen.hpp | 8 +- .../func/power/pow_impl/CMakeLists.txt | 2 + .../generic/func/power/pow_impl/pow_impl.hpp | 889 +++- .../func/power/pow_impl/pow_simd_impl.hpp | 300 ++ .../generic/func/power/pow_impl/powf_impl.hpp | 136 +- .../func/power/pow_impl/powf_simd_impl.hpp | 187 + .../func/power/pow_impl/powl_ld80_kernel.hpp | 302 ++ .../func/power/pow_impl/powl_ld80_tables.hpp | 485 +++ .../math/generic/func/power/powl_gen.hpp | 415 ++ .../math/generic/func/power/sqrt_gen.hpp | 23 +- .../math/generic/func/trig/cos_gen.hpp | 8 +- .../math/generic/func/trig/sin_gen.hpp | 8 +- .../ccmath/internal/math/runtime/README.md | 15 +- .../math/runtime/func/basic/fabs_rt.hpp | 15 +- .../math/runtime/func/basic/fdim_rt.hpp | 15 +- .../math/runtime/func/basic/fma_rt.hpp | 14 +- .../math/runtime/func/basic/fmax_rt.hpp | 15 +- .../math/runtime/func/basic/fmin_rt.hpp | 15 +- .../math/runtime/func/basic/fmod_rt.hpp | 15 +- .../math/runtime/func/basic/remainder_rt.hpp | 29 +- .../math/runtime/func/detail/msvc_libm.hpp | 55 +- .../math/runtime/func/expo/exp2_rt.hpp | 13 +- .../math/runtime/func/expo/exp_rt.hpp | 15 +- .../math/runtime/func/expo/expm1_rt.hpp | 5 +- .../math/runtime/func/expo/log10_rt.hpp | 31 +- .../math/runtime/func/expo/log1p_rt.hpp | 20 +- .../math/runtime/func/expo/log2_rt.hpp | 31 +- .../math/runtime/func/expo/log_rt.hpp | 31 +- .../math/runtime/func/fmanip/copysign_rt.hpp | 15 +- .../math/runtime/func/fmanip/frexp_rt.hpp | 6 +- .../math/runtime/func/fmanip/ilogb_rt.hpp | 17 +- .../math/runtime/func/fmanip/ldexp_rt.hpp | 15 +- .../math/runtime/func/fmanip/logb_rt.hpp | 17 +- .../math/runtime/func/fmanip/modf_rt.hpp | 4 +- .../math/runtime/func/fmanip/nextafter_rt.hpp | 15 +- .../runtime/func/fmanip/nexttoward_rt.hpp | 15 +- .../math/runtime/func/fmanip/scalbn_rt.hpp | 17 +- .../math/runtime/func/misc/gamma_rt.hpp | 2 +- .../math/runtime/func/misc/lgamma_rt.hpp | 2 +- .../math/runtime/func/nearest/ceil_rt.hpp | 15 +- .../math/runtime/func/nearest/floor_rt.hpp | 15 +- .../runtime/func/nearest/nearbyint_rt.hpp | 15 +- .../math/runtime/func/nearest/rint_rt.hpp | 24 +- .../math/runtime/func/nearest/round_rt.hpp | 19 +- .../math/runtime/func/nearest/trunc_rt.hpp | 15 +- .../math/runtime/func/power/cbrt_rt.hpp | 19 +- .../math/runtime/func/power/hypot_rt.hpp | 19 +- .../math/runtime/func/power/pow_rt.hpp | 47 +- .../math/runtime/func/power/sqrt_rt.hpp | 21 +- .../math/runtime/func/rt_dispatch.hpp | 50 +- .../math/runtime/func/svml_dispatch.hpp | 9 +- .../math/runtime/func/trig/acos_rt.hpp | 17 +- .../math/runtime/func/trig/asin_rt.hpp | 17 +- .../math/runtime/func/trig/atan2_rt.hpp | 27 +- .../math/runtime/func/trig/atan_rt.hpp | 17 +- .../math/runtime/func/trig/cos_rt.hpp | 22 +- .../math/runtime/func/trig/sin_rt.hpp | 22 +- .../math/runtime/func/trig/tan_rt.hpp | 22 +- .../internal/math/runtime/pp/CMakeLists.txt | 24 +- .../internal/math/runtime/pp/arm_detail.hpp | 2 - .../math/runtime/pp/assume_aligned.hpp | 3 +- .../math/runtime/pp/constexpr_wrapper.hpp | 11 - .../internal/math/runtime/pp/conversion.hpp | 37 + .../internal/math/runtime/pp/debug_print.hpp | 32 - .../internal/math/runtime/pp/declaration.hpp | 192 + .../internal/math/runtime/pp/detail.hpp | 534 --- .../ccmath/internal/math/runtime/pp/flags.hpp | 212 +- .../internal/math/runtime/pp/fwddecl.hpp | 395 -- .../math/runtime/pp/mask_reductions.hpp | 68 + .../internal/math/runtime/pp/msvc_intrin.hpp | 1200 ++++++ .../ccmath/internal/math/runtime/pp/pp.hpp | 49 + .../internal/math/runtime/pp/reduce.hpp | 87 + .../internal/math/runtime/pp/reference.hpp | 43 + .../internal/math/runtime/pp/scalar.hpp | 105 + .../ccmath/internal/math/runtime/pp/simd.hpp | 199 +- .../internal/math/runtime/pp/simd_cat.hpp | 90 + .../internal/math/runtime/pp/simd_config.hpp | 12 +- .../internal/math/runtime/pp/simd_mask.hpp | 93 + .../internal/math/runtime/pp/simd_math.hpp | 98 + .../internal/math/runtime/pp/simd_meta.hpp | 33 - .../internal/math/runtime/pp/simd_old.hpp | 318 -- .../internal/math/runtime/pp/traits.hpp | 103 + .../ccmath/internal/math/runtime/pp/trap.hpp | 18 - .../internal/math/runtime/pp/utility.hpp | 200 + .../internal/math/runtime/pp/vec_detail.hpp | 2 - .../internal/math/runtime/pp/vec_ext.hpp | 538 +++ .../ccmath/internal/math/runtime/pp/where.hpp | 102 + .../internal/math/runtime/pp/x86_include.hpp | 13 - .../internal/math/runtime/simd/common.hpp | 82 +- .../math/runtime/simd/func/impl/neon/pow.hpp | 8 +- .../runtime/simd/func/impl/scalar/map.hpp | 8 +- .../runtime/simd/func/impl/scalar/pow.hpp | 4 +- .../runtime/simd/func/impl/scalar/sqrt.hpp | 4 +- .../math/runtime/simd/func/impl/sse2/pow.hpp | 4 +- .../math/runtime/simd/func/impl/sse4/pow.hpp | 4 +- .../math/runtime/simd/func/svml_ops.hpp | 112 +- .../math/runtime/simd/instructions/avx2.hpp | 24 +- .../math/runtime/simd/instructions/neon.hpp | 48 +- .../math/runtime/simd/instructions/scalar.hpp | 32 +- .../math/runtime/simd/instructions/sse2.hpp | 36 +- .../math/runtime/simd/instructions/sse4.hpp | 32 +- .../internal/math/runtime/simd/pack.hpp | 80 +- .../math/runtime/simd/vector_size.hpp | 32 +- include/ccmath/internal/predef/assume.hpp | 8 +- .../clang_compiler_suppression.hpp | 4 +- .../gcc_compiler_suppression.hpp | 2 +- .../msvc_compiler_suppression.hpp | 2 +- include/ccmath/internal/support/bits.hpp | 133 +- .../support/common_math_constants.hpp | 2 +- .../internal/support/fenv/fenv_support.hpp | 76 +- .../internal/support/fenv/rounding_mode.hpp | 54 +- .../support/floating_point_traits.hpp | 28 +- .../ccmath/internal/support/fp/CMakeLists.txt | 3 + include/ccmath/internal/support/fp/cast.hpp | 64 + .../support/fp/directional_rounding_utils.hpp | 8 +- .../support/fp/except_value_utils.hpp | 123 + include/ccmath/internal/support/fp/fma.hpp | 685 +++ .../ccmath/internal/support/fp/fp_bits.hpp | 68 +- .../ccmath/internal/support/fp/ops/add.hpp | 4 +- .../internal/support/fp/ops/add_or_sub.hpp | 15 +- .../ccmath/internal/support/fp/ops/mul.hpp | 10 +- .../ccmath/internal/support/fp/ops/sub.hpp | 4 +- .../ccmath/internal/support/helpers/exp10.hpp | 43 +- .../support/helpers/internal_ldexp.hpp | 7 +- .../internal/support/integer_literals.hpp | 38 +- .../ccmath/internal/support/math_support.hpp | 95 +- .../ccmath/internal/support/multiply_add.hpp | 61 +- include/ccmath/internal/support/poly_eval.hpp | 16 +- include/ccmath/internal/types/big_int.hpp | 440 +- .../ccmath/internal/types/double_double.hpp | 34 +- .../ccmath/internal/types/dyadic_float.hpp | 323 +- include/ccmath/internal/types/float128.hpp | 2 + .../internal/types/normalized_float.hpp | 225 + include/ccmath/math/CMakeLists.txt | 1 + include/ccmath/math/basic/fabs.hpp | 24 +- include/ccmath/math/basic/fdim.hpp | 21 +- include/ccmath/math/basic/fma.hpp | 158 +- include/ccmath/math/basic/fmod.hpp | 24 +- .../math/basic/impl/nan_double_impl.hpp | 30 +- .../ccmath/math/basic/impl/nan_float_impl.hpp | 18 +- .../math/basic/impl/nan_ldouble_impl.hpp | 18 +- .../math/basic/impl/remquo_double_impl.hpp | 4 +- .../math/basic/impl/remquo_float_impl.hpp | 4 +- .../math/basic/impl/remquo_ldouble_impl.hpp | 4 +- include/ccmath/math/basic/max.hpp | 23 +- include/ccmath/math/basic/min.hpp | 15 +- include/ccmath/math/basic/nan.hpp | 8 +- include/ccmath/math/basic/remainder.hpp | 12 +- include/ccmath/math/basic/remquo.hpp | 9 +- include/ccmath/math/compare/fpclassify.hpp | 2 + include/ccmath/math/compare/isfinite.hpp | 4 +- include/ccmath/math/compare/isgreater.hpp | 7 +- .../ccmath/math/compare/isgreaterequal.hpp | 7 +- include/ccmath/math/compare/isinf.hpp | 2 +- include/ccmath/math/compare/isless.hpp | 7 +- include/ccmath/math/compare/islessequal.hpp | 7 +- include/ccmath/math/compare/islessgreater.hpp | 7 +- include/ccmath/math/compare/isnan.hpp | 3 +- include/ccmath/math/compare/isnormal.hpp | 11 +- include/ccmath/math/compare/isunordered.hpp | 7 +- include/ccmath/math/compare/signbit.hpp | 3 +- include/ccmath/math/expo/exp.hpp | 22 +- include/ccmath/math/expo/exp2.hpp | 23 +- include/ccmath/math/expo/expm1.hpp | 21 +- .../math/expo/impl/exp2_double_impl.hpp | 4 +- .../ccmath/math/expo/impl/exp2_float_impl.hpp | 54 +- include/ccmath/math/expo/impl/expm1_impl.hpp | 8 +- include/ccmath/math/expo/impl/log10_impl.hpp | 16 +- include/ccmath/math/expo/impl/log1p_impl.hpp | 14 +- .../math/expo/impl/log2_double_impl.hpp | 8 +- .../ccmath/math/expo/impl/log2_float_impl.hpp | 8 +- include/ccmath/math/expo/impl/log_data.hpp | 2 +- .../ccmath/math/expo/impl/log_double_impl.hpp | 22 +- .../ccmath/math/expo/impl/log_float_impl.hpp | 10 +- include/ccmath/math/expo/log.hpp | 22 +- include/ccmath/math/expo/log10.hpp | 21 +- include/ccmath/math/expo/log1p.hpp | 23 +- include/ccmath/math/expo/log2.hpp | 29 +- include/ccmath/math/fmanip.hpp | 2 + include/ccmath/math/fmanip/CMakeLists.txt | 2 + include/ccmath/math/fmanip/copysign.hpp | 14 +- include/ccmath/math/fmanip/frexp.hpp | 39 +- include/ccmath/math/fmanip/ilogb.hpp | 20 +- .../ccmath/math/fmanip/impl/CMakeLists.txt | 3 - .../math/fmanip/impl/scalbn_double_impl.hpp | 8 +- .../math/fmanip/impl/scalbn_float_impl.hpp | 8 +- .../math/fmanip/impl/scalbn_ldouble_impl.hpp | 8 +- include/ccmath/math/fmanip/ldexp.hpp | 34 +- include/ccmath/math/fmanip/logb.hpp | 20 +- include/ccmath/math/fmanip/modf.hpp | 10 +- include/ccmath/math/fmanip/nextafter.hpp | 20 +- include/ccmath/math/fmanip/nextdown.hpp | 33 + include/ccmath/math/fmanip/nexttoward.hpp | 20 +- include/ccmath/math/fmanip/nextup.hpp | 33 + include/ccmath/math/fmanip/scalbn.hpp | 42 +- include/ccmath/math/misc/gamma.hpp | 26 +- .../math/misc/impl/gamma_double_impl.hpp | 5 +- include/ccmath/math/misc/impl/gamma_impl.hpp | 8 +- .../math/misc/impl/lgamma_double_impl.hpp | 8 +- include/ccmath/math/misc/impl/lgamma_impl.hpp | 8 +- include/ccmath/math/misc/lerp.hpp | 1 - include/ccmath/math/misc/lgamma.hpp | 12 +- include/ccmath/math/nearest/ceil.hpp | 16 +- include/ccmath/math/nearest/floor.hpp | 67 +- include/ccmath/math/nearest/nearbyint.hpp | 12 +- include/ccmath/math/nearest/rint.hpp | 12 +- include/ccmath/math/nearest/round.hpp | 16 +- include/ccmath/math/nearest/trunc.hpp | 19 +- include/ccmath/math/power/CMakeLists.txt | 2 - include/ccmath/math/power/cbrt.hpp | 20 +- include/ccmath/math/power/hypot.hpp | 48 +- include/ccmath/math/power/pow.hpp | 84 +- include/ccmath/math/power/sqrt.hpp | 14 +- include/ccmath/math/trig/acos.hpp | 24 +- include/ccmath/math/trig/asin.hpp | 24 +- include/ccmath/math/trig/atan.hpp | 24 +- include/ccmath/math/trig/atan2.hpp | 20 +- include/ccmath/math/trig/cos.hpp | 14 +- .../ccmath/math/trig/impl/inv_trig_impl.hpp | 116 +- .../math/trig/impl/sincos_double_data.hpp | 65 +- .../math/trig/impl/sincos_double_impl.hpp | 90 +- .../math/trig/impl/sincos_float_data.hpp | 25 +- .../math/trig/impl/sincos_float_impl.hpp | 68 +- .../math/trig/impl/sincos_payne_hanek.hpp | 150 + include/ccmath/math/trig/sin.hpp | 14 +- include/ccmath/math/trig/tan.hpp | 14 +- integration/vendored-consumer/.gitignore | 8 + integration/vendored-consumer/CMakeLists.txt | 12 + .../vendored-consumer/main.cpp | 12 +- integration/vendored-consumer/meson.build | 15 + integration/vendored-consumer/premake5.lua | 20 + meson.build | 19 + meson_options.txt | 7 + premake5.lua | 102 + showcase/CMakeLists.txt | 22 + showcase/README.md | 82 + showcase/accuracy/CMakeLists.txt | 3 + showcase/accuracy/showcase_accuracy.cpp | 88 + showcase/common/accuracy/corpus.hpp | 32 + .../common/accuracy/unary_mpfr_runner.hpp | 216 + showcase/common/backend_traits.hpp | 21 + showcase/common/backends.hpp | 5 + showcase/common/backends/ccmath_backend.hpp | 76 + showcase/common/backends/gcem_backend.hpp | 35 + showcase/common/backends/std_backend.hpp | 17 + showcase/common/bench/bench_common.hpp | 22 + showcase/common/ccmath_paths.hpp | 152 + showcase/common/compile/probe_template.cpp.in | 10 + showcase/config/backends.json | 20 + showcase/config/functions.json | 44 + showcase/item/README.md | 45 + showcase/item/accuracy/gcem-sin.run.xml | 8 + showcase/item/accuracy/gcem-sqrt.run.xml | 8 + showcase/item/accuracy/sin-all.run.xml | 8 + showcase/item/accuracy/sin-generic.run.xml | 8 + showcase/item/accuracy/sin-public.run.xml | 8 + showcase/item/accuracy/sqrt-all.run.xml | 8 + showcase/item/accuracy/sqrt-generic.run.xml | 8 + showcase/item/accuracy/sqrt-public.run.xml | 8 + showcase/item/accuracy/sqrt-rt.run.xml | 8 + showcase/item/build.run.xml | 11 + showcase/item/doctor.run.xml | 11 + showcase/item/perf/sin.run.xml | 8 + showcase/item/perf/sqrt.run.xml | 8 + showcase/item/suite/accuracy.run.xml | 11 + showcase/item/suite/all.run.xml | 11 + showcase/item/suite/compile.run.xml | 11 + showcase/item/suite/perf.run.xml | 11 + showcase/performance/CMakeLists.txt | 8 + showcase/performance/sin.bench.cpp | 41 + showcase/performance/sqrt.bench.cpp | 41 + showcase/tools/measure_compile.py | 164 + showcase/tools/paths.py | 71 + showcase/tools/paths_cli.py | 54 + showcase/tools/report_schema.json | 11 + showcase/tools/showcase.py | 96 + tests/CMakeLists.txt | 157 +- tests/README.md | 35 + tests/basic/fdim_test.cpp | 173 - tests/basic/fma_test.cpp | 187 - tests/compare/fpclassify_test.cpp | 41 - tests/compare/islessgreater_test.cpp | 29 - tests/compare/isnormal_test.cpp | 30 - tests/compare/signbit_test.cpp | 31 - tests/conformance/fenv_exception_test.cpp | 42 +- tests/conformance/fenv_probe_test.cpp | 4 +- tests/conformance/fma_rounding_test.cpp | 56 + tests/conformance/fma_software_fenv_test.cpp | 139 + tests/conformance/include_policy_test.cpp | 70 + tests/conformance/pow_errno_fenv_test.cpp | 193 + tests/conformance/pow_path_config_test.cpp | 64 + tests/conformance/powl_platform_test.cpp | 46 + .../conformance/rounding_conformance_test.cpp | 117 +- tests/conformance/ulp_all_modes_test.cpp | 37 +- tests/conformance/ulp_distance_test.cpp | 58 + tests/conformance/worst_case_ulp_test.cpp | 136 +- tests/nearest/nearbyint_test.cpp | 279 -- tests/power/pow_test.cpp | 35 - tests/power/ulp_accuracy_test.cpp | 109 - tests/proofs/README.md | 50 + .../proofs/math/power/pow_accuracy_probe.cpp | 207 + tests/proofs/math/power/pow_double_exp_eval.g | 43 + tests/proofs/math/power/pow_double_log_eval.g | 56 + tests/proofs/math/power/pow_double_proof.py | 402 ++ tests/proofs/math/power/pow_float_exp_eval.g | 50 + tests/proofs/math/power/pow_float_log_eval.g | 50 + tests/proofs/math/power/pow_float_proof.py | 251 ++ .../math/power/proof_freshness_check.py | 122 + tests/proofs/math/power/proof_reporting.py | 307 ++ tests/rigorous/CMakeLists.txt | 50 + tests/rigorous/README.md | 40 + tests/shared/oracle/README.md | 19 + tests/shared/oracle/big_int_gmp.cpp | 289 ++ .../shared/oracle/coremath_oracle_harness.hpp | 128 + tests/shared/oracle/coremath_pow_cases.hpp | 20 + tests/shared/oracle/coremath_pow_common.hpp | 162 + tests/shared/oracle/coremath_pow_double.cpp | 4 + tests/shared/oracle/coremath_pow_float.cpp | 4 + tests/shared/oracle/cross_libm_common.hpp | 86 + tests/shared/oracle/cross_libm_pow.cpp | 119 + tests/shared/oracle/mpfr_fma_common.hpp | 292 ++ tests/shared/oracle/mpfr_fma_double.cpp | 4 + tests/shared/oracle/mpfr_fma_float.cpp | 4 + tests/shared/oracle/mpfr_oracle_harness.hpp | 632 +++ tests/shared/oracle/mpfr_pow_common.hpp | 96 + tests/shared/oracle/mpfr_pow_double.cpp | 4 + tests/shared/oracle/mpfr_pow_float.cpp | 4 + tests/shared/oracle/mpfr_powl.cpp | 352 ++ .../shared/oracle/oracle_campaign_common.hpp | 661 +++ .../oracle/oracle_campaign_metadata.hpp | 20 + .../shared/oracle/oracle_campaign_ternary.hpp | 63 + tests/shared/oracle/oracle_campaign_unary.hpp | 66 + tests/shared/oracle/pow_adversarial_modes.hpp | 269 ++ .../shared/oracle/pow_adversarial_search.cpp | 128 + tests/shared/oracle/pow_cases.hpp | 271 ++ .../shared/oracle/powf_exhaustive_domains.hpp | 141 + tests/shared/oracle/powl_path_reporting.hpp | 138 + .../oracle/powl_platform_characterization.cpp | 146 + .../oracle/runners/coremath_pow_runner.hpp | 140 + .../shared/oracle/runners/mpfr_pow_runner.hpp | 175 + tests/shared/utils/campaign_metadata.hpp | 165 + .../{ => shared}/utils/conformance_suite.hpp | 110 +- tests/{ => shared}/utils/fenv_fixture.hpp | 30 +- tests/{ => shared}/utils/fp_matcher.hpp | 5 +- tests/{ => shared}/utils/helpers.hpp | 58 +- tests/shared/utils/math_samples.hpp | 251 ++ tests/shared/utils/pow_path_dispatch.hpp | 163 + tests/{ => shared}/utils/std_compare.hpp | 5 +- tests/shared/utils/test_runtime.hpp | 20 + tests/shared/utils/ulp_distance.hpp | 105 + tests/{ => shared}/utils/ulp_suite.hpp | 23 +- tests/shared/utils/worst_case_samples.hpp | 200 + tests/src/ext/abs_diff_test.cpp | 23 + tests/src/ext/align_test.cpp | 26 + tests/src/ext/approximately_test.cpp | 22 + tests/src/ext/ceil_div_test.cpp | 24 + tests/src/ext/chgsign_test.cpp | 26 + tests/src/ext/clamp_test.cpp | 27 + tests/src/ext/constexpr_smoke_test.cpp | 90 + tests/src/ext/cubic_test.cpp | 21 + tests/src/ext/degrees_test.cpp | 21 + tests/src/ext/delta_angle_test.cpp | 22 + .../ext/fract_test.cpp} | 15 +- tests/src/ext/inverse_lerp_test.cpp | 22 + tests/src/ext/is_power_of_two_test.cpp | 29 + tests/src/ext/lerp_angle_test.cpp | 21 + tests/src/ext/lerp_smooth_test.cpp | 24 + tests/src/ext/mix_test.cpp | 23 + tests/src/ext/move_towards_angle_test.cpp | 24 + tests/src/ext/move_towards_test.cpp | 23 + tests/src/ext/normalize_test.cpp | 24 + tests/src/ext/ping_pong_test.cpp | 23 + tests/src/ext/radians_test.cpp | 21 + .../ext/rcp_test.cpp} | 11 +- tests/src/ext/remap_test.cpp | 22 + tests/src/ext/repeat_test.cpp | 23 + tests/src/ext/saturate_test.cpp | 23 + tests/src/ext/sign_test.cpp | 27 + tests/src/ext/smoothstep_test.cpp | 24 + tests/src/ext/step_test.cpp | 24 + tests/src/ext/unlerp_test.cpp | 22 + tests/{ => src/math}/basic/abs_test.cpp | 18 +- tests/src/math/basic/fdim_test.cpp | 183 + .../fma_constexpr_upward_config_test.cpp | 25 + .../math/basic/fma_correct_rounding_test.cpp | 140 + tests/src/math/basic/fma_test.cpp | 149 + tests/{ => src/math}/basic/fmod_test.cpp | 16 +- tests/{ => src/math}/basic/max_test.cpp | 0 tests/{ => src/math}/basic/min_test.cpp | 9 +- tests/{ => src/math}/basic/nan_test.cpp | 41 +- tests/{ => src/math}/basic/remainder_test.cpp | 11 +- tests/{ => src/math}/basic/remquo_test.cpp | 44 +- tests/src/math/compare/fpclassify_test.cpp | 42 + .../{ => src/math}/compare/isfinite_test.cpp | 4 +- .../{ => src/math}/compare/isgreater_test.cpp | 3 +- .../math}/compare/isgreaterequal_test.cpp | 6 +- tests/{ => src/math}/compare/isinf_test.cpp | 6 +- tests/{ => src/math}/compare/isless_test.cpp | 6 +- .../math}/compare/islessequal_test.cpp | 6 +- tests/src/math/compare/islessgreater_test.cpp | 30 + tests/{ => src/math}/compare/isnan_test.cpp | 8 +- tests/src/math/compare/isnormal_test.cpp | 31 + .../math}/compare/isunordered_test.cpp | 21 +- tests/src/math/compare/signbit_test.cpp | 49 + tests/src/math/constexpr_smoke_test.cpp | 118 + .../math/expo}/exp2_test.cpp | 12 +- .../math/expo}/exp_test.cpp | 28 +- .../math/expo}/expm1_test.cpp | 8 +- .../math/expo/expo_ulp_accuracy_test.cpp} | 48 +- .../math/expo}/log10_test.cpp | 18 + .../math/expo}/log1p_test.cpp | 0 .../math/expo}/log2_test.cpp | 5 +- .../math/expo}/log_test.cpp | 31 +- tests/{ => src/math}/fmanip/copysign_test.cpp | 8 +- tests/{ => src/math}/fmanip/frexp_test.cpp | 18 +- tests/{ => src/math}/fmanip/ilogb_test.cpp | 10 +- tests/{ => src/math}/fmanip/ldexp_test.cpp | 3 +- tests/{ => src/math}/fmanip/logb_test.cpp | 8 +- tests/{ => src/math}/fmanip/modf_test.cpp | 6 +- tests/src/math/fmanip/nextafter_test.cpp | 37 + .../{ => src/math}/fmanip/nexttoward_test.cpp | 10 +- tests/{ => src/math}/fmanip/scalbn_test.cpp | 26 +- tests/src/math/internal/simd/pp_simd_test.cpp | 535 +++ .../internal/support/math_support_test.cpp | 275 ++ .../src/math/internal/types/big_int_test.cpp | 992 +++++ .../math/internal/types/dyadic_float_test.cpp | 54 + .../internal/types/normalized_float_test.cpp | 59 + tests/{ => src/math}/misc/gamma_test.cpp | 29 +- .../{ => src/math}/misc/lerp_test_no_std.cpp | 8 +- tests/{ => src/math}/misc/lerp_test_std.cpp | 14 +- tests/{ => src/math}/misc/lgamma_test.cpp | 0 .../math}/nearest/ceil_round_rint_test.cpp | 10 +- tests/{ => src/math}/nearest/floor_test.cpp | 47 +- tests/src/math/nearest/nearbyint_test.cpp | 279 ++ tests/{ => src/math}/nearest/trunc_test.cpp | 138 +- .../{ => src/math}/power/hypot_cbrt_test.cpp | 7 + .../math/power/pow_correct_rounding_test.cpp | 107 + .../src/math/power/pow_deterministic_test.cpp | 98 + .../math/power/pow_integer_boundary_test.cpp | 128 + tests/src/math/power/pow_simd_test.cpp | 307 ++ tests/src/math/power/pow_test.cpp | 644 +++ .../math/power/power_ulp_accuracy_test.cpp | 384 ++ tests/src/math/power/powf_simd_test.cpp | 198 + .../math/power/powl_fallback_policy_test.cpp | 106 + .../math/power/powl_format_detection_test.cpp | 61 + tests/src/math/power/powl_ld64_alias_test.cpp | 56 + .../math/power/powl_ld80_bounded_int_test.cpp | 87 + .../power/powl_ld80_general_finite_test.cpp | 87 + .../power/powl_ld80_integer_class_test.cpp | 88 + .../power/powl_ld80_special_cases_test.cpp | 86 + .../math/power/powl_unsupported_tier_test.cpp | 68 + tests/{ => src/math}/power/sqrt_test.cpp | 12 +- .../math/trig}/inverse_trig_test.cpp | 0 .../math/trig}/sin_test.cpp | 0 .../math/trig/trig_kernel_regression_test.cpp | 103 + .../src/math/trig/trig_ulp_accuracy_test.cpp | 70 + tests/trigonometric/ulp_accuracy_test.cpp | 92 - tests/unit/CMakeLists.txt | 78 + tests/utils/math_samples.hpp | 131 - tests/utils/worst_case_samples.hpp | 210 - thirdparty/CMakeLists.txt | 19 +- thirdparty/README.md | 52 + tools/apply_format.sh | 15 + tools/asmlab/CMakeLists.txt | 45 + tools/asmlab/README.md | 104 + tools/asmlab/accuracy/CMakeLists.txt | 33 + .../accuracy/candidate_accuracy_powf_impl.cpp | 163 + tools/asmlab/bench/CMakeLists.txt | 45 + tools/asmlab/bench/golden_poly_bench.cpp | 34 + tools/asmlab/bench/powf_impl_bench.cpp | 84 + tools/asmlab/docker/Dockerfile | 29 + tools/asmlab/docker/run.sh | 87 + tools/asmlab/golden/harness.cpp.in | 21 + tools/asmlab/golden/pair_expected.json | 30 + tools/asmlab/golden/poly_estrin/expected.json | 22 + tools/asmlab/golden/poly_estrin/kernel.hpp | 41 + tools/asmlab/golden/poly_horner/expected.json | 18 + tools/asmlab/golden/poly_horner/kernel.hpp | 40 + tools/asmlab/harness/constexpr_probe.cpp.in | 20 + tools/asmlab/harness/harness.cpp.in | 35 + tools/asmlab/knowledge/README.md | 33 + .../knowledge/calibration/apple_m1.json | 38 + .../knowledge/calibration/x86_64_v3.json | 32 + .../instruction_facts/aarch64/fmadd.json | 17 + .../instruction_facts/aarch64/fsqrt.json | 14 + .../aarch64/stack_patterns.json | 33 + .../knowledge/instruction_facts/x86/fma.json | 23 + .../knowledge/instruction_facts/x86/sqrt.json | 23 + .../instruction_facts/x86/stack_patterns.json | 19 + .../asmlab/knowledge/microarchitectures.json | 111 + .../aarch64_stack_serial_fma.json | 14 + .../back_to_back_fma_dependency.json | 10 + .../call_boundary_thin_slice.json | 14 + .../asmlab/knowledge/pattern_rules/index.json | 9 + .../pattern_rules/x86_stack_serial_fma.json | 14 + tools/asmlab/knowledge/schema.json | 60 + tools/asmlab/knowledge/sources.json | 125 + .../asmlab/refdata/.gitkeep | 0 tools/asmlab/registry/accuracy_manifest.json | 90 + tools/asmlab/registry/benchmark_profiles.json | 59 + .../registry/candidate_edge_case_inputs.json | 121 + .../asmlab/registry/candidate_edge_cases.json | 96 + tools/asmlab/registry/functions.json | 141 + tools/asmlab/registry/golden_targets.json | 23 + tools/asmlab/registry/path_categories.json | 29 + tools/asmlab/registry/path_scenarios.json | 105 + tools/asmlab/registry/variant_targets.json | 15 + tools/asmlab/rigorous_gate.sh | 33 + tools/asmlab/scripts/_asmlab_common.py | 345 ++ tools/asmlab/scripts/accuracy_gate.sh | 202 + tools/asmlab/scripts/analysis_common.py | 72 + tools/asmlab/scripts/analysis_diff.py | 117 + tools/asmlab/scripts/analysis_pipeline.py | 1287 ++++++ tools/asmlab/scripts/analyze.py | 213 + tools/asmlab/scripts/asm_diff.py | 210 + tools/asmlab/scripts/asmlab.py | 1019 +++++ tools/asmlab/scripts/bench.sh | 240 ++ tools/asmlab/scripts/bench_impl.py | 286 ++ tools/asmlab/scripts/bootstrap.sh | 97 + tools/asmlab/scripts/candidate_compare.py | 823 ++++ tools/asmlab/scripts/classify.py | 317 ++ tools/asmlab/scripts/constexpr_check.py | 120 + tools/asmlab/scripts/cpu_notes.py | 504 +++ tools/asmlab/scripts/emit.py | 269 ++ tools/asmlab/scripts/godbolt.py | 157 + tools/asmlab/scripts/golden_emit.py | 123 + tools/asmlab/scripts/golden_findings.py | 353 ++ tools/asmlab/scripts/run_golden_analysis.py | 235 ++ tools/asmlab/scripts/run_meta.py | 118 + tools/asmlab/scripts/scenario.py | 699 ++++ tools/asmlab/scripts/source_map.py | 525 +++ tools/asmlab/scripts/test_bench_wiring.py | 71 + tools/asmlab/scripts/test_golden_quick.py | 64 + tools/asmlab/scripts/variant.py | 403 ++ tools/asmlab/scripts/variant_accuracy.py | 341 ++ tools/asmlab/scripts/view.py | 313 ++ tools/clang_format_version | 1 + tools/coremath/build_prefix.sh | 38 + tools/download_with_retry.sh | 51 + tools/ensure_format.sh | 38 + tools/format.py | 12 +- tools/gen_secondary_builds.py | 542 +++ tools/install_clang_format_linux.sh | 28 + tools/install_premake5_linux.sh | 42 + tools/integration/prepare_vendored_tree.sh | 38 + tools/plot.py | 30 +- tools/premake5 | 21 + .../generate_powl_ld80_native_tables.sollya | 34 + .../math/power/generate_powl_ld80_tables.py | 175 + .../power/generate_powl_ld80_tables.sollya | 51 + .../power/run_validate_powl_ld80_kernel.sh | 14 + .../power/validate_powl_ld80_kernel.sollya | 70 + tools/run_format.sh | 13 +- 887 files changed, 58869 insertions(+), 9848 deletions(-) create mode 100644 .github/actions/install-clang-format/action.yml create mode 100644 .github/workflows/ci-integration.yml delete mode 100644 .github/workflows/dependency-review.yml create mode 100644 benchmarks/README.md delete mode 100644 benchmarks/benchmarks/basic/abs.bench.cpp delete mode 100644 benchmarks/benchmarks/basic/abs.bench.hpp delete mode 100644 benchmarks/benchmarks/basic/fdim.bench.cpp delete mode 100644 benchmarks/benchmarks/basic/fdim.bench.hpp delete mode 100644 benchmarks/benchmarks/basic/fma.bench.cpp delete mode 100644 benchmarks/benchmarks/basic/fma.bench.hpp delete mode 100644 benchmarks/benchmarks/nearest/trunc.bench.hpp delete mode 100644 benchmarks/benchmarks/power/sqrt.bench.cpp delete mode 100644 benchmarks/benchmarks/power/sqrt.bench.hpp delete mode 100644 benchmarks/ccmath_benchmark_main.cpp delete mode 100644 benchmarks/helpers/randomizers.hpp create mode 100644 benchmarks/shared/harness.hpp create mode 100644 benchmarks/shared/inputs.hpp create mode 100644 benchmarks/shared/paths.hpp create mode 100644 benchmarks/shared/register.hpp create mode 100644 benchmarks/src/math/basic/abs.bench.cpp create mode 100644 benchmarks/src/math/basic/fdim.bench.cpp create mode 100644 benchmarks/src/math/basic/fma.bench.cpp create mode 100644 benchmarks/src/math/nearest/trunc.bench.cpp create mode 100644 benchmarks/src/math/power/sqrt.bench.cpp delete mode 100644 benchmarks/tools/plot.py create mode 100644 cmake/README.md create mode 100644 cmake/config/BenchmarkModuleRegistry.cmake create mode 100644 cmake/config/BuildManifest.cmake create mode 100644 cmake/config/EmitBuildManifest.cmake create mode 100644 cmake/config/ExampleModuleRegistry.cmake create mode 100644 cmake/config/FindCoreMath.cmake create mode 100644 cmake/config/FindMpfrGmp.cmake create mode 100644 cmake/config/FuzzModuleRegistry.cmake create mode 100644 cmake/config/OracleCampaignRegistry.cmake create mode 100644 cmake/config/ShowcaseDeps.cmake create mode 100644 cmake/config/TestCtestTargets.cmake create mode 100644 cmake/config/TestModuleRegistry.cmake create mode 100644 cmake/config/TestOracleTargets.cmake create mode 100644 cmake/config/TestValidationOptions.cmake create mode 100644 cmake/coremath/CMakeLists.txt create mode 100644 cmake/coremath/crmath.h create mode 100644 cmake/features/simd/SelectSimdInstructionSet.cmake create mode 100644 cmake/gen/meson.build create mode 100644 cmake/helpers/CcmBenchHelpers.cmake create mode 100644 cmake/helpers/CcmExampleHelpers.cmake create mode 100644 cmake/helpers/CcmFuzzHelpers.cmake create mode 100644 cmake/helpers/CcmOptionalTooling.cmake create mode 100644 cmake/helpers/CcmOracleHelpers.cmake create mode 100644 cmake/helpers/CcmTestHelpers.cmake create mode 100644 cmake/vcpkg/vcpkg.json create mode 100644 docs/ROUNDING_STATUS.md create mode 100644 docs/ULP_STATUS.md create mode 100644 docs/approximating_functions/ACM-Reference-Format.bst create mode 100644 docs/approximating_functions/APPROXIMATING_FUNCTIONS.pdf create mode 100644 docs/approximating_functions/APPROXIMATING_FUNCTIONS.tex create mode 100644 docs/approximating_functions/README.md create mode 100644 docs/approximating_functions/approx_functions.bib create mode 100755 docs/approximating_functions/build.sh create mode 100644 docs/approximating_functions/cpp/directed_rounding_demo.cpp create mode 100644 docs/approximating_functions/cpp/sin_small_check.cpp create mode 100644 docs/approximating_functions/gappa/sin_small_eval.gappa create mode 100644 docs/approximating_functions/sollya/exp_small_fpminimax.sollya create mode 100644 docs/approximating_functions/sollya/hard_case_scan.sollya create mode 100644 docs/approximating_functions/sollya/log2_core_points.sollya create mode 100644 docs/approximating_functions/sollya/log2_core_poly.sollya create mode 100644 docs/approximating_functions/sollya/log2_core_table.sollya create mode 100644 docs/approximating_functions/sollya/sin_minimax_compare.sollya create mode 100644 docs/approximating_functions/sollya/sin_small_error.sollya create mode 100644 docs/approximating_functions/sollya/sin_small_fpminimax.sollya create mode 100644 docs/approximating_functions/sollya/sin_taylor_degree_error.sollya create mode 100644 docs/approximating_functions/sollya/sin_taylor_error.sollya create mode 100644 docs/proofs/power/POW_PROOF.tex create mode 100644 examples/README.md create mode 100644 examples/common/io.hpp rename examples/{lowest_supported_compiler_per_function.md => docs/compiler_support.md} (100%) create mode 100644 examples/item/README.md create mode 100644 examples/item/constexpr_abs.run.xml create mode 100644 examples/item/constexpr_smoke.run.xml create mode 100644 examples/item/decibel_gain.run.xml create mode 100644 examples/item/fp_classes.run.xml create mode 100644 examples/item/hypot_distance.run.xml create mode 100644 examples/item/rounding_edge_cases.run.xml create mode 100644 examples/item/signed_magnitude.run.xml create mode 100644 examples/item/unit_circle.run.xml delete mode 100644 examples/main.cpp create mode 100644 examples/src/constexpr_abs.cpp create mode 100644 examples/src/constexpr_smoke.cpp create mode 100644 examples/src/decibel_gain.cpp create mode 100644 examples/src/fp_classes.cpp create mode 100644 examples/src/hypot_distance.cpp create mode 100644 examples/src/rounding_edge_cases.cpp create mode 100644 examples/src/signed_magnitude.cpp create mode 100644 examples/src/unit_circle.cpp create mode 100644 fuzzing/README.md delete mode 100644 fuzzing/basic_fuzz.cpp rename fuzzing/{ => cmake}/run_fuzz_campaign.cmake (54%) create mode 100644 fuzzing/cmake/run_fuzz_smoke.cmake delete mode 100644 fuzzing/compare_fuzz.cpp create mode 100644 fuzzing/dicts/ccmath.dict delete mode 100644 fuzzing/expo_fuzz.cpp delete mode 100644 fuzzing/fmanip_fuzz.cpp delete mode 100644 fuzzing/generate_fuzz_seeds.cmake delete mode 100644 fuzzing/nearest_fuzz.cpp delete mode 100644 fuzzing/power_fuzz.cpp delete mode 100644 fuzzing/run_fuzz_smoke.cmake create mode 100644 fuzzing/scripts/generate_seeds.py create mode 100644 fuzzing/shared/entry.hpp create mode 100644 fuzzing/shared/fuzz_calls.hpp rename fuzzing/{ => shared}/harness.hpp (77%) create mode 100644 fuzzing/shared/input.hpp create mode 100644 fuzzing/src/math/basic_fuzz.cpp create mode 100644 fuzzing/src/math/compare_fuzz.cpp create mode 100644 fuzzing/src/math/expo_fuzz.cpp create mode 100644 fuzzing/src/math/fmanip_fuzz.cpp rename fuzzing/{ => src/math}/misc_fuzz.cpp (50%) create mode 100644 fuzzing/src/math/nearest_fuzz.cpp create mode 100644 fuzzing/src/math/power_fuzz.cpp create mode 100644 fuzzing/src/math/trig_fuzz.cpp delete mode 100644 fuzzing/trig_fuzz.cpp create mode 100644 include/ccmath/ext/abs_diff.hpp create mode 100644 include/ccmath/ext/approximately.hpp create mode 100644 include/ccmath/ext/ceil_div.hpp create mode 100644 include/ccmath/ext/chgsign.hpp create mode 100644 include/ccmath/ext/delta_angle.hpp create mode 100644 include/ccmath/ext/inverse_lerp.hpp create mode 100644 include/ccmath/ext/lerp_angle.hpp create mode 100644 include/ccmath/ext/move_towards.hpp create mode 100644 include/ccmath/ext/move_towards_angle.hpp create mode 100644 include/ccmath/ext/remap.hpp create mode 100644 include/ccmath/ext/repeat.hpp create mode 100644 include/ccmath/ext/saturate.hpp create mode 100644 include/ccmath/ext/sign.hpp create mode 100644 include/ccmath/ext/step.hpp create mode 100644 include/ccmath/ext/unlerp.hpp create mode 100644 include/ccmath/internal/config/long_double_format.hpp create mode 100644 include/ccmath/internal/config/powl_policy.hpp create mode 100644 include/ccmath/internal/math/generic/builtins/nearest/nearbyint.hpp create mode 100644 include/ccmath/internal/math/generic/builtins/nearest/rint.hpp delete mode 100644 include/ccmath/internal/math/generic/func/expo/exp2_impl/CMakeLists.txt delete mode 100644 include/ccmath/internal/math/generic/func/expo/exp2_impl/exp2_impl.hpp delete mode 100644 include/ccmath/internal/math/generic/func/expo/exp2_impl/exp2f_impl.hpp create mode 100644 include/ccmath/internal/math/generic/func/fmanip/impl/CMakeLists.txt rename include/ccmath/{math => internal/math/generic/func}/fmanip/impl/frexp_impl.hpp (100%) rename include/ccmath/{math => internal/math/generic/func}/fmanip/impl/ilogb_impl.hpp (95%) rename include/ccmath/{math => internal/math/generic/func}/fmanip/impl/logb_impl.hpp (92%) rename include/ccmath/{math => internal/math/generic/func}/power/impl/CMakeLists.txt (100%) rename include/ccmath/{math => internal/math/generic/func}/power/impl/cbrt_impl.hpp (100%) rename include/ccmath/{math => internal/math/generic/func}/power/impl/hypot_impl.hpp (100%) create mode 100644 include/ccmath/internal/math/generic/func/power/pow_impl/pow_simd_impl.hpp create mode 100644 include/ccmath/internal/math/generic/func/power/pow_impl/powf_simd_impl.hpp create mode 100644 include/ccmath/internal/math/generic/func/power/pow_impl/powl_ld80_kernel.hpp create mode 100644 include/ccmath/internal/math/generic/func/power/pow_impl/powl_ld80_tables.hpp create mode 100644 include/ccmath/internal/math/generic/func/power/powl_gen.hpp delete mode 100644 include/ccmath/internal/math/runtime/pp/arm_detail.hpp delete mode 100644 include/ccmath/internal/math/runtime/pp/constexpr_wrapper.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/conversion.hpp delete mode 100644 include/ccmath/internal/math/runtime/pp/debug_print.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/declaration.hpp delete mode 100644 include/ccmath/internal/math/runtime/pp/detail.hpp delete mode 100644 include/ccmath/internal/math/runtime/pp/fwddecl.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/mask_reductions.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/msvc_intrin.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/pp.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/reduce.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/reference.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/scalar.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/simd_cat.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/simd_mask.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/simd_math.hpp delete mode 100644 include/ccmath/internal/math/runtime/pp/simd_meta.hpp delete mode 100644 include/ccmath/internal/math/runtime/pp/simd_old.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/traits.hpp delete mode 100644 include/ccmath/internal/math/runtime/pp/trap.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/utility.hpp delete mode 100644 include/ccmath/internal/math/runtime/pp/vec_detail.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/vec_ext.hpp create mode 100644 include/ccmath/internal/math/runtime/pp/where.hpp delete mode 100644 include/ccmath/internal/math/runtime/pp/x86_include.hpp create mode 100644 include/ccmath/internal/support/fp/cast.hpp create mode 100644 include/ccmath/internal/support/fp/except_value_utils.hpp create mode 100644 include/ccmath/internal/support/fp/fma.hpp create mode 100644 include/ccmath/math/fmanip/nextdown.hpp create mode 100644 include/ccmath/math/fmanip/nextup.hpp create mode 100644 include/ccmath/math/trig/impl/sincos_payne_hanek.hpp create mode 100644 integration/vendored-consumer/.gitignore create mode 100644 integration/vendored-consumer/CMakeLists.txt rename tests/hyperbolic/_test.cpp => integration/vendored-consumer/main.cpp (63%) create mode 100644 integration/vendored-consumer/meson.build create mode 100644 integration/vendored-consumer/premake5.lua create mode 100644 meson.build create mode 100644 meson_options.txt create mode 100644 premake5.lua create mode 100644 showcase/CMakeLists.txt create mode 100644 showcase/README.md create mode 100644 showcase/accuracy/CMakeLists.txt create mode 100644 showcase/accuracy/showcase_accuracy.cpp create mode 100644 showcase/common/accuracy/corpus.hpp create mode 100644 showcase/common/accuracy/unary_mpfr_runner.hpp create mode 100644 showcase/common/backend_traits.hpp create mode 100644 showcase/common/backends.hpp create mode 100644 showcase/common/backends/ccmath_backend.hpp create mode 100644 showcase/common/backends/gcem_backend.hpp create mode 100644 showcase/common/backends/std_backend.hpp create mode 100644 showcase/common/bench/bench_common.hpp create mode 100644 showcase/common/ccmath_paths.hpp create mode 100644 showcase/common/compile/probe_template.cpp.in create mode 100644 showcase/config/backends.json create mode 100644 showcase/config/functions.json create mode 100644 showcase/item/README.md create mode 100644 showcase/item/accuracy/gcem-sin.run.xml create mode 100644 showcase/item/accuracy/gcem-sqrt.run.xml create mode 100644 showcase/item/accuracy/sin-all.run.xml create mode 100644 showcase/item/accuracy/sin-generic.run.xml create mode 100644 showcase/item/accuracy/sin-public.run.xml create mode 100644 showcase/item/accuracy/sqrt-all.run.xml create mode 100644 showcase/item/accuracy/sqrt-generic.run.xml create mode 100644 showcase/item/accuracy/sqrt-public.run.xml create mode 100644 showcase/item/accuracy/sqrt-rt.run.xml create mode 100644 showcase/item/build.run.xml create mode 100644 showcase/item/doctor.run.xml create mode 100644 showcase/item/perf/sin.run.xml create mode 100644 showcase/item/perf/sqrt.run.xml create mode 100644 showcase/item/suite/accuracy.run.xml create mode 100644 showcase/item/suite/all.run.xml create mode 100644 showcase/item/suite/compile.run.xml create mode 100644 showcase/item/suite/perf.run.xml create mode 100644 showcase/performance/CMakeLists.txt create mode 100644 showcase/performance/sin.bench.cpp create mode 100644 showcase/performance/sqrt.bench.cpp create mode 100644 showcase/tools/measure_compile.py create mode 100644 showcase/tools/paths.py create mode 100644 showcase/tools/paths_cli.py create mode 100644 showcase/tools/report_schema.json create mode 100644 showcase/tools/showcase.py create mode 100644 tests/README.md delete mode 100644 tests/basic/fdim_test.cpp delete mode 100644 tests/basic/fma_test.cpp delete mode 100644 tests/compare/fpclassify_test.cpp delete mode 100644 tests/compare/islessgreater_test.cpp delete mode 100644 tests/compare/isnormal_test.cpp delete mode 100644 tests/compare/signbit_test.cpp create mode 100644 tests/conformance/fma_rounding_test.cpp create mode 100644 tests/conformance/fma_software_fenv_test.cpp create mode 100644 tests/conformance/include_policy_test.cpp create mode 100644 tests/conformance/pow_errno_fenv_test.cpp create mode 100644 tests/conformance/pow_path_config_test.cpp create mode 100644 tests/conformance/powl_platform_test.cpp create mode 100644 tests/conformance/ulp_distance_test.cpp delete mode 100644 tests/nearest/nearbyint_test.cpp delete mode 100644 tests/power/pow_test.cpp delete mode 100644 tests/power/ulp_accuracy_test.cpp create mode 100644 tests/proofs/README.md create mode 100644 tests/proofs/math/power/pow_accuracy_probe.cpp create mode 100644 tests/proofs/math/power/pow_double_exp_eval.g create mode 100644 tests/proofs/math/power/pow_double_log_eval.g create mode 100644 tests/proofs/math/power/pow_double_proof.py create mode 100644 tests/proofs/math/power/pow_float_exp_eval.g create mode 100644 tests/proofs/math/power/pow_float_log_eval.g create mode 100644 tests/proofs/math/power/pow_float_proof.py create mode 100644 tests/proofs/math/power/proof_freshness_check.py create mode 100644 tests/proofs/math/power/proof_reporting.py create mode 100644 tests/rigorous/CMakeLists.txt create mode 100644 tests/rigorous/README.md create mode 100644 tests/shared/oracle/README.md create mode 100644 tests/shared/oracle/big_int_gmp.cpp create mode 100644 tests/shared/oracle/coremath_oracle_harness.hpp create mode 100644 tests/shared/oracle/coremath_pow_cases.hpp create mode 100644 tests/shared/oracle/coremath_pow_common.hpp create mode 100644 tests/shared/oracle/coremath_pow_double.cpp create mode 100644 tests/shared/oracle/coremath_pow_float.cpp create mode 100644 tests/shared/oracle/cross_libm_common.hpp create mode 100644 tests/shared/oracle/cross_libm_pow.cpp create mode 100644 tests/shared/oracle/mpfr_fma_common.hpp create mode 100644 tests/shared/oracle/mpfr_fma_double.cpp create mode 100644 tests/shared/oracle/mpfr_fma_float.cpp create mode 100644 tests/shared/oracle/mpfr_oracle_harness.hpp create mode 100644 tests/shared/oracle/mpfr_pow_common.hpp create mode 100644 tests/shared/oracle/mpfr_pow_double.cpp create mode 100644 tests/shared/oracle/mpfr_pow_float.cpp create mode 100644 tests/shared/oracle/mpfr_powl.cpp create mode 100644 tests/shared/oracle/oracle_campaign_common.hpp create mode 100644 tests/shared/oracle/oracle_campaign_metadata.hpp create mode 100644 tests/shared/oracle/oracle_campaign_ternary.hpp create mode 100644 tests/shared/oracle/oracle_campaign_unary.hpp create mode 100644 tests/shared/oracle/pow_adversarial_modes.hpp create mode 100644 tests/shared/oracle/pow_adversarial_search.cpp create mode 100644 tests/shared/oracle/pow_cases.hpp create mode 100644 tests/shared/oracle/powf_exhaustive_domains.hpp create mode 100644 tests/shared/oracle/powl_path_reporting.hpp create mode 100644 tests/shared/oracle/powl_platform_characterization.cpp create mode 100644 tests/shared/oracle/runners/coremath_pow_runner.hpp create mode 100644 tests/shared/oracle/runners/mpfr_pow_runner.hpp create mode 100644 tests/shared/utils/campaign_metadata.hpp rename tests/{ => shared}/utils/conformance_suite.hpp (55%) rename tests/{ => shared}/utils/fenv_fixture.hpp (82%) rename tests/{ => shared}/utils/fp_matcher.hpp (95%) rename tests/{ => shared}/utils/helpers.hpp (61%) create mode 100644 tests/shared/utils/math_samples.hpp create mode 100644 tests/shared/utils/pow_path_dispatch.hpp rename tests/{ => shared}/utils/std_compare.hpp (98%) create mode 100644 tests/shared/utils/test_runtime.hpp create mode 100644 tests/shared/utils/ulp_distance.hpp rename tests/{ => shared}/utils/ulp_suite.hpp (83%) create mode 100644 tests/shared/utils/worst_case_samples.hpp create mode 100644 tests/src/ext/abs_diff_test.cpp create mode 100644 tests/src/ext/align_test.cpp create mode 100644 tests/src/ext/approximately_test.cpp create mode 100644 tests/src/ext/ceil_div_test.cpp create mode 100644 tests/src/ext/chgsign_test.cpp create mode 100644 tests/src/ext/clamp_test.cpp create mode 100644 tests/src/ext/constexpr_smoke_test.cpp create mode 100644 tests/src/ext/cubic_test.cpp create mode 100644 tests/src/ext/degrees_test.cpp create mode 100644 tests/src/ext/delta_angle_test.cpp rename tests/{internal/types/big_int_test.cpp => src/ext/fract_test.cpp} (52%) create mode 100644 tests/src/ext/inverse_lerp_test.cpp create mode 100644 tests/src/ext/is_power_of_two_test.cpp create mode 100644 tests/src/ext/lerp_angle_test.cpp create mode 100644 tests/src/ext/lerp_smooth_test.cpp create mode 100644 tests/src/ext/mix_test.cpp create mode 100644 tests/src/ext/move_towards_angle_test.cpp create mode 100644 tests/src/ext/move_towards_test.cpp create mode 100644 tests/src/ext/normalize_test.cpp create mode 100644 tests/src/ext/ping_pong_test.cpp create mode 100644 tests/src/ext/radians_test.cpp rename tests/{fmanip/nextafter_test.cpp => src/ext/rcp_test.cpp} (53%) create mode 100644 tests/src/ext/remap_test.cpp create mode 100644 tests/src/ext/repeat_test.cpp create mode 100644 tests/src/ext/saturate_test.cpp create mode 100644 tests/src/ext/sign_test.cpp create mode 100644 tests/src/ext/smoothstep_test.cpp create mode 100644 tests/src/ext/step_test.cpp create mode 100644 tests/src/ext/unlerp_test.cpp rename tests/{ => src/math}/basic/abs_test.cpp (96%) create mode 100644 tests/src/math/basic/fdim_test.cpp create mode 100644 tests/src/math/basic/fma_constexpr_upward_config_test.cpp create mode 100644 tests/src/math/basic/fma_correct_rounding_test.cpp create mode 100644 tests/src/math/basic/fma_test.cpp rename tests/{ => src/math}/basic/fmod_test.cpp (94%) rename tests/{ => src/math}/basic/max_test.cpp (100%) rename tests/{ => src/math}/basic/min_test.cpp (97%) rename tests/{ => src/math}/basic/nan_test.cpp (91%) rename tests/{ => src/math}/basic/remainder_test.cpp (90%) rename tests/{ => src/math}/basic/remquo_test.cpp (68%) create mode 100644 tests/src/math/compare/fpclassify_test.cpp rename tests/{ => src/math}/compare/isfinite_test.cpp (93%) rename tests/{ => src/math}/compare/isgreater_test.cpp (93%) rename tests/{ => src/math}/compare/isgreaterequal_test.cpp (88%) rename tests/{ => src/math}/compare/isinf_test.cpp (96%) rename tests/{ => src/math}/compare/isless_test.cpp (88%) rename tests/{ => src/math}/compare/islessequal_test.cpp (88%) create mode 100644 tests/src/math/compare/islessgreater_test.cpp rename tests/{ => src/math}/compare/isnan_test.cpp (84%) create mode 100644 tests/src/math/compare/isnormal_test.cpp rename tests/{ => src/math}/compare/isunordered_test.cpp (54%) create mode 100644 tests/src/math/compare/signbit_test.cpp create mode 100644 tests/src/math/constexpr_smoke_test.cpp rename tests/{exponential => src/math/expo}/exp2_test.cpp (99%) rename tests/{exponential => src/math/expo}/exp_test.cpp (77%) rename tests/{exponential => src/math/expo}/expm1_test.cpp (100%) rename tests/{exponential/ulp_accuracy_test.cpp => src/math/expo/expo_ulp_accuracy_test.cpp} (67%) rename tests/{exponential => src/math/expo}/log10_test.cpp (51%) rename tests/{exponential => src/math/expo}/log1p_test.cpp (100%) rename tests/{exponential => src/math/expo}/log2_test.cpp (99%) rename tests/{exponential => src/math/expo}/log_test.cpp (53%) rename tests/{ => src/math}/fmanip/copysign_test.cpp (97%) rename tests/{ => src/math}/fmanip/frexp_test.cpp (85%) rename tests/{ => src/math}/fmanip/ilogb_test.cpp (100%) rename tests/{ => src/math}/fmanip/ldexp_test.cpp (99%) rename tests/{ => src/math}/fmanip/logb_test.cpp (100%) rename tests/{ => src/math}/fmanip/modf_test.cpp (100%) create mode 100644 tests/src/math/fmanip/nextafter_test.cpp rename tests/{ => src/math}/fmanip/nexttoward_test.cpp (83%) rename tests/{ => src/math}/fmanip/scalbn_test.cpp (65%) create mode 100644 tests/src/math/internal/simd/pp_simd_test.cpp create mode 100644 tests/src/math/internal/support/math_support_test.cpp create mode 100644 tests/src/math/internal/types/big_int_test.cpp create mode 100644 tests/src/math/internal/types/dyadic_float_test.cpp create mode 100644 tests/src/math/internal/types/normalized_float_test.cpp rename tests/{ => src/math}/misc/gamma_test.cpp (54%) rename tests/{ => src/math}/misc/lerp_test_no_std.cpp (83%) rename tests/{ => src/math}/misc/lerp_test_std.cpp (82%) rename tests/{ => src/math}/misc/lgamma_test.cpp (100%) rename tests/{ => src/math}/nearest/ceil_round_rint_test.cpp (93%) rename tests/{ => src/math}/nearest/floor_test.cpp (63%) create mode 100644 tests/src/math/nearest/nearbyint_test.cpp rename tests/{ => src/math}/nearest/trunc_test.cpp (51%) rename tests/{ => src/math}/power/hypot_cbrt_test.cpp (91%) create mode 100644 tests/src/math/power/pow_correct_rounding_test.cpp create mode 100644 tests/src/math/power/pow_deterministic_test.cpp create mode 100644 tests/src/math/power/pow_integer_boundary_test.cpp create mode 100644 tests/src/math/power/pow_simd_test.cpp create mode 100644 tests/src/math/power/pow_test.cpp create mode 100644 tests/src/math/power/power_ulp_accuracy_test.cpp create mode 100644 tests/src/math/power/powf_simd_test.cpp create mode 100644 tests/src/math/power/powl_fallback_policy_test.cpp create mode 100644 tests/src/math/power/powl_format_detection_test.cpp create mode 100644 tests/src/math/power/powl_ld64_alias_test.cpp create mode 100644 tests/src/math/power/powl_ld80_bounded_int_test.cpp create mode 100644 tests/src/math/power/powl_ld80_general_finite_test.cpp create mode 100644 tests/src/math/power/powl_ld80_integer_class_test.cpp create mode 100644 tests/src/math/power/powl_ld80_special_cases_test.cpp create mode 100644 tests/src/math/power/powl_unsupported_tier_test.cpp rename tests/{ => src/math}/power/sqrt_test.cpp (97%) rename tests/{trigonometric => src/math/trig}/inverse_trig_test.cpp (100%) rename tests/{trigonometric => src/math/trig}/sin_test.cpp (100%) create mode 100644 tests/src/math/trig/trig_kernel_regression_test.cpp create mode 100644 tests/src/math/trig/trig_ulp_accuracy_test.cpp delete mode 100644 tests/trigonometric/ulp_accuracy_test.cpp create mode 100644 tests/unit/CMakeLists.txt delete mode 100644 tests/utils/math_samples.hpp delete mode 100644 tests/utils/worst_case_samples.hpp create mode 100644 thirdparty/README.md create mode 100644 tools/apply_format.sh create mode 100644 tools/asmlab/CMakeLists.txt create mode 100644 tools/asmlab/README.md create mode 100644 tools/asmlab/accuracy/CMakeLists.txt create mode 100644 tools/asmlab/accuracy/candidate_accuracy_powf_impl.cpp create mode 100644 tools/asmlab/bench/CMakeLists.txt create mode 100644 tools/asmlab/bench/golden_poly_bench.cpp create mode 100644 tools/asmlab/bench/powf_impl_bench.cpp create mode 100644 tools/asmlab/docker/Dockerfile create mode 100755 tools/asmlab/docker/run.sh create mode 100644 tools/asmlab/golden/harness.cpp.in create mode 100644 tools/asmlab/golden/pair_expected.json create mode 100644 tools/asmlab/golden/poly_estrin/expected.json create mode 100644 tools/asmlab/golden/poly_estrin/kernel.hpp create mode 100644 tools/asmlab/golden/poly_horner/expected.json create mode 100644 tools/asmlab/golden/poly_horner/kernel.hpp create mode 100644 tools/asmlab/harness/constexpr_probe.cpp.in create mode 100644 tools/asmlab/harness/harness.cpp.in create mode 100644 tools/asmlab/knowledge/README.md create mode 100644 tools/asmlab/knowledge/calibration/apple_m1.json create mode 100644 tools/asmlab/knowledge/calibration/x86_64_v3.json create mode 100644 tools/asmlab/knowledge/instruction_facts/aarch64/fmadd.json create mode 100644 tools/asmlab/knowledge/instruction_facts/aarch64/fsqrt.json create mode 100644 tools/asmlab/knowledge/instruction_facts/aarch64/stack_patterns.json create mode 100644 tools/asmlab/knowledge/instruction_facts/x86/fma.json create mode 100644 tools/asmlab/knowledge/instruction_facts/x86/sqrt.json create mode 100644 tools/asmlab/knowledge/instruction_facts/x86/stack_patterns.json create mode 100644 tools/asmlab/knowledge/microarchitectures.json create mode 100644 tools/asmlab/knowledge/pattern_rules/aarch64_stack_serial_fma.json create mode 100644 tools/asmlab/knowledge/pattern_rules/back_to_back_fma_dependency.json create mode 100644 tools/asmlab/knowledge/pattern_rules/call_boundary_thin_slice.json create mode 100644 tools/asmlab/knowledge/pattern_rules/index.json create mode 100644 tools/asmlab/knowledge/pattern_rules/x86_stack_serial_fma.json create mode 100644 tools/asmlab/knowledge/schema.json create mode 100644 tools/asmlab/knowledge/sources.json rename benchmarks/benchmarks/nearest/trunc.bench.cpp => tools/asmlab/refdata/.gitkeep (100%) create mode 100644 tools/asmlab/registry/accuracy_manifest.json create mode 100644 tools/asmlab/registry/benchmark_profiles.json create mode 100644 tools/asmlab/registry/candidate_edge_case_inputs.json create mode 100644 tools/asmlab/registry/candidate_edge_cases.json create mode 100644 tools/asmlab/registry/functions.json create mode 100644 tools/asmlab/registry/golden_targets.json create mode 100644 tools/asmlab/registry/path_categories.json create mode 100644 tools/asmlab/registry/path_scenarios.json create mode 100644 tools/asmlab/registry/variant_targets.json create mode 100755 tools/asmlab/rigorous_gate.sh create mode 100755 tools/asmlab/scripts/_asmlab_common.py create mode 100755 tools/asmlab/scripts/accuracy_gate.sh create mode 100644 tools/asmlab/scripts/analysis_common.py create mode 100644 tools/asmlab/scripts/analysis_diff.py create mode 100644 tools/asmlab/scripts/analysis_pipeline.py create mode 100755 tools/asmlab/scripts/analyze.py create mode 100644 tools/asmlab/scripts/asm_diff.py create mode 100755 tools/asmlab/scripts/asmlab.py create mode 100755 tools/asmlab/scripts/bench.sh create mode 100644 tools/asmlab/scripts/bench_impl.py create mode 100755 tools/asmlab/scripts/bootstrap.sh create mode 100644 tools/asmlab/scripts/candidate_compare.py create mode 100644 tools/asmlab/scripts/classify.py create mode 100644 tools/asmlab/scripts/constexpr_check.py create mode 100644 tools/asmlab/scripts/cpu_notes.py create mode 100755 tools/asmlab/scripts/emit.py create mode 100755 tools/asmlab/scripts/godbolt.py create mode 100644 tools/asmlab/scripts/golden_emit.py create mode 100644 tools/asmlab/scripts/golden_findings.py create mode 100644 tools/asmlab/scripts/run_golden_analysis.py create mode 100644 tools/asmlab/scripts/run_meta.py create mode 100644 tools/asmlab/scripts/scenario.py create mode 100644 tools/asmlab/scripts/source_map.py create mode 100644 tools/asmlab/scripts/test_bench_wiring.py create mode 100644 tools/asmlab/scripts/test_golden_quick.py create mode 100644 tools/asmlab/scripts/variant.py create mode 100644 tools/asmlab/scripts/variant_accuracy.py create mode 100644 tools/asmlab/scripts/view.py create mode 100644 tools/clang_format_version create mode 100755 tools/coremath/build_prefix.sh create mode 100755 tools/download_with_retry.sh create mode 100644 tools/ensure_format.sh create mode 100644 tools/gen_secondary_builds.py create mode 100755 tools/install_clang_format_linux.sh create mode 100755 tools/install_premake5_linux.sh create mode 100644 tools/integration/prepare_vendored_tree.sh create mode 100755 tools/premake5 create mode 100644 tools/proofs/math/power/generate_powl_ld80_native_tables.sollya create mode 100644 tools/proofs/math/power/generate_powl_ld80_tables.py create mode 100644 tools/proofs/math/power/generate_powl_ld80_tables.sollya create mode 100644 tools/proofs/math/power/run_validate_powl_ld80_kernel.sh create mode 100644 tools/proofs/math/power/validate_powl_ld80_kernel.sollya diff --git a/.github/actions/install-clang-format/action.yml b/.github/actions/install-clang-format/action.yml new file mode 100644 index 00000000..f359f74d --- /dev/null +++ b/.github/actions/install-clang-format/action.yml @@ -0,0 +1,48 @@ +name: Install clang-format +description: Install clang-format 22.1.6 for CI format checks with download retries on macOS and Windows + +runs: + using: composite + steps: + - name: Read clang-format version + id: clang_format_version + shell: bash + run: echo "value=$(tr -d '[:space:]' < tools/clang_format_version)" >> "${GITHUB_OUTPUT}" + + - name: Install clang-format (Linux) + if: runner.os == 'Linux' + shell: bash + run: bash tools/install_clang_format_linux.sh + + - name: Install clang-format (attempt 1) + if: runner.os != 'Linux' + uses: aminya/setup-cpp@1f17f92d6a52bfcb1a25348e2c526c2e5cbb1134 # v1.8.0 + continue-on-error: true + id: clang_format_attempt_1 + with: + clangformat: ${{ steps.clang_format_version.outputs.value }} + + - name: Wait before clang-format retry + if: runner.os != 'Linux' && steps.clang_format_attempt_1.outcome != 'success' + shell: bash + run: sleep 30 + + - name: Install clang-format (attempt 2) + if: runner.os != 'Linux' && steps.clang_format_attempt_1.outcome != 'success' + uses: aminya/setup-cpp@1f17f92d6a52bfcb1a25348e2c526c2e5cbb1134 # v1.8.0 + continue-on-error: true + id: clang_format_attempt_2 + with: + clangformat: ${{ steps.clang_format_version.outputs.value }} + + - name: Wait before clang-format retry + if: runner.os != 'Linux' && steps.clang_format_attempt_1.outcome != 'success' && steps.clang_format_attempt_2.outcome != 'success' + shell: bash + run: sleep 60 + + - name: Install clang-format (attempt 3) + if: runner.os != 'Linux' && steps.clang_format_attempt_1.outcome != 'success' && steps.clang_format_attempt_2.outcome != 'success' + uses: aminya/setup-cpp@1f17f92d6a52bfcb1a25348e2c526c2e5cbb1134 # v1.8.0 + id: clang_format_attempt_3 + with: + clangformat: ${{ steps.clang_format_version.outputs.value }} diff --git a/.github/workflows/ci-integration.yml b/.github/workflows/ci-integration.yml new file mode 100644 index 00000000..65fb2236 --- /dev/null +++ b/.github/workflows/ci-integration.yml @@ -0,0 +1,85 @@ +name: ci-integration + +on: + push: + tags-ignore: + - v*.* + branches: + - '**' + pull_request: + branches: + - '**' + +permissions: + contents: read + +jobs: + vendored-integration: + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Install dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -yqq --no-install-recommends ninja-build meson + + - name: Cache premake5 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: .ci-cache/premake + key: premake-5.0.0-beta8-linux + + - name: Install premake5 + env: + PREMAKE_CACHE_DIR: ${{ github.workspace }}/.ci-cache/premake + run: | + mkdir -p .ci-cache/premake + bash tools/install_premake5_linux.sh .ci-cache/premake + sudo install -m 755 .ci-cache/premake/premake5 /usr/local/bin/premake5 + + - name: Configure CCMath with CMake + run: | + cmake --preset ninja-clang-debug \ + -DCCMATH_BUILD_TESTS=OFF \ + -DCCMATH_BUILD_EXAMPLES=OFF \ + -DCCMATH_BUILD_BENCHMARKS=OFF + + - name: Generate secondary build manifest + run: cmake --build out/build/ninja/ninja-clang-debug --target ccmath-generate-secondary-builds + + - name: Verify secondary build markers + run: | + python3 tools/gen_secondary_builds.py \ + --check \ + --manifest out/build/ninja/ninja-clang-debug/ccmath-build-manifest.json \ + --source-dir . \ + --output-dir out/build/ninja/ninja-clang-debug/gen-secondary + + - name: Prepare vendored CCMath tree + run: bash tools/integration/prepare_vendored_tree.sh out/build/ninja/ninja-clang-debug + + - name: CMake vendored integration + working-directory: integration/vendored-consumer + run: | + cmake -B build-cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug + cmake --build build-cmake + ./build-cmake/vendored-ccmath-consumer + + - name: Meson vendored integration + run: | + meson setup integration/vendored-consumer/build-meson integration/vendored-consumer + meson compile -C integration/vendored-consumer/build-meson + ./integration/vendored-consumer/build-meson/vendored-ccmath-consumer + + - name: Premake vendored integration + working-directory: integration/vendored-consumer + run: | + premake5 gmake + make config=debug + ./bin/Debug/vendored-ccmath-consumer diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index da5c6b73..e5ee4046 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -5,67 +5,111 @@ on: tags-ignore: - v*.* branches: - - '*' + - '**' pull_request: branches: - - '*' + - '**' permissions: contents: read jobs: - build: + build-cxx17: runs-on: ubuntu-latest + timeout-minutes: 30 strategy: - fail-fast: false + fail-fast: true matrix: include: - # GCC Debug - name: gcc-debug configurePreset: ninja-gcc-debug buildPreset: build-ninja-gcc-debug testPreset: test-ninja-gcc-debug cxx_version: 17 - - name: gcc-debug-cxx20 - configurePreset: ninja-gcc-debug - buildPreset: build-ninja-gcc-debug - testPreset: test-ninja-gcc-debug - cxx_version: 20 - - # GCC Release - name: gcc-release configurePreset: ninja-gcc-release buildPreset: build-ninja-gcc-release testPreset: test-ninja-gcc-release cxx_version: 17 - - name: gcc-release-cxx20 - configurePreset: ninja-gcc-release - buildPreset: build-ninja-gcc-release - testPreset: test-ninja-gcc-release - cxx_version: 20 - - # Clang Debug - name: clang-debug configurePreset: ninja-clang-debug buildPreset: build-ninja-clang-debug testPreset: test-ninja-clang-debug cxx_version: 17 - - name: clang-debug-cxx20 - configurePreset: ninja-clang-debug - buildPreset: build-ninja-clang-debug - testPreset: test-ninja-clang-debug - cxx_version: 20 - - # Clang Release - name: clang-release configurePreset: ninja-clang-release buildPreset: build-ninja-clang-release testPreset: test-ninja-clang-release cxx_version: 17 + steps: + - name: Harden Runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Cache build + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: | + ./**/CMakeFiles + ./**/CMakeCache.txt + ./**/out + key: ${{ runner.os }}-${{ matrix.name }}-${{ hashFiles('CMakeLists.txt', 'CMakePresets.json', 'cmake/**', 'include/**', 'tests/**', 'thirdparty/**') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.name }}- + + - name: Install dependencies + run: sudo apt-get update -qq && sudo apt-get install -yqq --no-install-recommends ninja-build + + - name: Configure + run: > + cmake --preset="${{ matrix.configurePreset }}" + -DCCMATH_DESIRED_CXX_STANDARD="cxx_std_${{ matrix.cxx_version }}" + -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON + -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + -DCCMATH_BUILD_SIMPLE_TESTS=ON + -DCCMATH_BUILD_RIGOROUS_TESTS=OFF + -DCCMATH_ENABLE_MPFR_TESTS=OFF + -DCCMATH_ENABLE_POW_SEARCH_TOOLS=OFF + + - name: Build + run: cmake --build --preset="${{ matrix.buildPreset }}" + + - name: Test + run: ctest --preset="${{ matrix.testPreset }}" --output-on-failure --schedule-random -L simple -F + + build-cxx20: + needs: build-cxx17 + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - name: gcc-debug-cxx20 + configurePreset: ninja-gcc-debug + buildPreset: build-ninja-gcc-debug + testPreset: test-ninja-gcc-debug + cxx_version: 20 + + - name: gcc-release-cxx20 + configurePreset: ninja-gcc-release + buildPreset: build-ninja-gcc-release + testPreset: test-ninja-gcc-release + cxx_version: 20 + + - name: clang-debug-cxx20 + configurePreset: ninja-clang-debug + buildPreset: build-ninja-clang-debug + testPreset: test-ninja-clang-debug + cxx_version: 20 + - name: clang-release-cxx20 configurePreset: ninja-clang-release buildPreset: build-ninja-clang-release @@ -74,29 +118,128 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@v2.16.0 + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: egress-policy: audit - - uses: actions/checkout@v4 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Cache build - uses: actions/cache@v4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | ./**/CMakeFiles ./**/CMakeCache.txt ./**/out - key: ${{ runner.os }}-${{ matrix.name }} + key: ${{ runner.os }}-${{ matrix.name }}-${{ hashFiles('CMakeLists.txt', 'CMakePresets.json', 'cmake/**', 'include/**', 'tests/**', 'thirdparty/**') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.name }}- - name: Install dependencies run: sudo apt-get update -qq && sudo apt-get install -yqq --no-install-recommends ninja-build - name: Configure - run: cmake --preset="${{ matrix.configurePreset }}" -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + run: > + cmake --preset="${{ matrix.configurePreset }}" + -DCCMATH_DESIRED_CXX_STANDARD="cxx_std_${{ matrix.cxx_version }}" + -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON + -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + -DCCMATH_BUILD_SIMPLE_TESTS=ON + -DCCMATH_BUILD_RIGOROUS_TESTS=OFF + -DCCMATH_ENABLE_MPFR_TESTS=OFF + -DCCMATH_ENABLE_POW_SEARCH_TOOLS=OFF - name: Build run: cmake --build --preset="${{ matrix.buildPreset }}" - name: Test - run: ctest --preset="${{ matrix.testPreset }}" --output-on-failure --schedule-random -F + run: ctest --preset="${{ matrix.testPreset }}" --output-on-failure --schedule-random -L simple -F + + rigorous: + needs: build-cxx17 + runs-on: ubuntu-latest + timeout-minutes: 45 + + steps: + - name: Harden Runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Cache rigorous builds + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: | + build-rigorous + build-rigorous-nobuiltin + key: ${{ runner.os }}-rigorous-build-${{ hashFiles('CMakeLists.txt', 'cmake/**', 'tests/**', 'include/**', 'thirdparty/**') }} + restore-keys: | + ${{ runner.os }}-rigorous-build- + + - name: Cache APT archives + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: /var/cache/apt/archives + key: ${{ runner.os }}-apt-mpfr-gmp-v1 + + - name: Cache CORE-MATH prefix + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: coremath-prefix + key: ${{ runner.os }}-coremath-prefix-v1-${{ hashFiles('cmake/coremath/**', 'tools/coremath/**') }} + + - name: Install dependencies + run: sudo apt-get update -qq && sudo apt-get install -yqq --no-install-recommends ninja-build pkg-config libmpfr-dev libgmp-dev sollya gappa git || sudo apt-get install -yqq --no-install-recommends ninja-build pkg-config libmpfr-dev libgmp-dev git + + - name: Build CORE-MATH oracle prefix + run: | + if [[ ! -f coremath-prefix/include/crmath.h ]]; then + bash tools/coremath/build_prefix.sh "${GITHUB_WORKSPACE}/coremath-prefix" + fi + + - name: Configure rigorous validation + run: > + cmake -S . -B build-rigorous -G Ninja + -DCCMATH_BUILD_TESTS=ON + -DCCMATH_BUILD_SIMPLE_TESTS=OFF + -DCCMATH_BUILD_RIGOROUS_TESTS=ON + -DCCMATH_ENABLE_MPFR_TESTS=ON + -DCCMATH_ENABLE_COREMATH_TESTS=ON + -DCCMATH_COREMATH_ROOT=${{ github.workspace }}/coremath-prefix + -DCCMATH_ENABLE_POW_SEARCH_TOOLS=ON + -DCCMATH_DESIRED_CXX_STANDARD=cxx_std_20 + -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON + -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + + - name: Configure rigorous no-builtin validation + run: > + cmake -S . -B build-rigorous-nobuiltin -G Ninja + -DCCMATH_BUILD_TESTS=ON + -DCCMATH_BUILD_SIMPLE_TESTS=OFF + -DCCMATH_BUILD_RIGOROUS_TESTS=ON + -DCCMATH_ENABLE_MPFR_TESTS=ON + -DCCMATH_ENABLE_COREMATH_TESTS=ON + -DCCMATH_COREMATH_ROOT=${{ github.workspace }}/coremath-prefix + -DCCMATH_ENABLE_POW_SEARCH_TOOLS=ON + -DCCMATH_TEST_DISABLE_RUNTIME_BUILTIN=ON + -DCCMATH_DESIRED_CXX_STANDARD=cxx_std_20 + -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON + -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + + - name: Build rigorous validation + run: cmake --build build-rigorous --parallel + + - name: Build rigorous no-builtin validation + run: cmake --build build-rigorous-nobuiltin --parallel + + - name: Run rigorous validation + run: ctest --test-dir build-rigorous --output-on-failure -L rigorous + + - name: Run rigorous no-builtin validation + run: ctest --test-dir build-rigorous-nobuiltin --output-on-failure -L rigorous + + - name: Run pow proof smoke + continue-on-error: true + run: cmake --build build-rigorous --target ccmath-rigorous-pow-proof-smoke diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index 3febe67d..5ea10c82 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -5,19 +5,20 @@ on: tags-ignore: - v*.* branches: - - '*' + - '**' pull_request: branches: - - '*' + - '**' permissions: contents: read jobs: - build: + build-cxx17: runs-on: macos-latest + timeout-minutes: 30 strategy: - fail-fast: false + fail-fast: true matrix: include: - name: clang-debug-cxx17 @@ -26,42 +27,86 @@ jobs: testPreset: test-ninja-clang-debug cxx_version: 17 - - name: clang-debug-cxx20 - configurePreset: ninja-clang-debug - buildPreset: build-ninja-clang-debug - testPreset: test-ninja-clang-debug - cxx_version: 20 - - name: clang-release-cxx17 configurePreset: ninja-clang-release buildPreset: build-ninja-clang-release testPreset: test-ninja-clang-release cxx_version: 17 - - name: clang-release-cxx20 - configurePreset: ninja-clang-release - buildPreset: build-ninja-clang-release - testPreset: test-ninja-clang-release - cxx_version: 20 - - name: gcc-debug-cxx17 configurePreset: ninja-gcc-debug buildPreset: build-ninja-gcc-debug testPreset: test-ninja-gcc-debug cxx_version: 17 - - name: gcc-debug-cxx20 - configurePreset: ninja-gcc-debug - buildPreset: build-ninja-gcc-debug - testPreset: test-ninja-gcc-debug - cxx_version: 20 - - name: gcc-release-cxx17 configurePreset: ninja-gcc-release buildPreset: build-ninja-gcc-release testPreset: test-ninja-gcc-release cxx_version: 17 + steps: + - name: Harden Runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Cache build + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: | + ./**/CMakeFiles + ./**/CMakeCache.txt + ./**/out + key: ${{ runner.os }}-${{ matrix.name }}-${{ hashFiles('CMakeLists.txt', 'CMakePresets.json', 'cmake/**', 'include/**', 'tests/**', 'thirdparty/**') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.name }}- + + - name: Configure + run: > + cmake --preset="${{ matrix.configurePreset }}" + -DCCMATH_DESIRED_CXX_STANDARD="cxx_std_${{ matrix.cxx_version }}" + -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON + -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + -DCCMATH_BUILD_SIMPLE_TESTS=ON + -DCCMATH_BUILD_RIGOROUS_TESTS=OFF + -DCCMATH_ENABLE_MPFR_TESTS=OFF + -DCCMATH_ENABLE_POW_SEARCH_TOOLS=OFF + + - name: Build + run: cmake --build --preset="${{ matrix.buildPreset }}" + + - name: Test + run: ctest --preset="${{ matrix.testPreset }}" --output-on-failure --schedule-random -L simple -F + + build-cxx20: + needs: build-cxx17 + runs-on: macos-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - name: clang-debug-cxx20 + configurePreset: ninja-clang-debug + buildPreset: build-ninja-clang-debug + testPreset: test-ninja-clang-debug + cxx_version: 20 + + - name: clang-release-cxx20 + configurePreset: ninja-clang-release + buildPreset: build-ninja-clang-release + testPreset: test-ninja-clang-release + cxx_version: 20 + + - name: gcc-debug-cxx20 + configurePreset: ninja-gcc-debug + buildPreset: build-ninja-gcc-debug + testPreset: test-ninja-gcc-debug + cxx_version: 20 + - name: gcc-release-cxx20 configurePreset: ninja-gcc-release buildPreset: build-ninja-gcc-release @@ -70,29 +115,99 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@v2.16.0 + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: egress-policy: audit - - uses: actions/checkout@v4 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Cache build - uses: actions/cache@v4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | ./**/CMakeFiles ./**/CMakeCache.txt ./**/out - key: ${{ runner.os }}-${{ matrix.name }} - - - name: Install Ninja - run: brew install ninja + key: ${{ runner.os }}-${{ matrix.name }}-${{ hashFiles('CMakeLists.txt', 'CMakePresets.json', 'cmake/**', 'include/**', 'tests/**', 'thirdparty/**') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.name }}- - name: Configure - run: cmake --preset="${{ matrix.configurePreset }}" -DCCMATH_DESIRED_CXX_STANDARD="cxx_std_${{ matrix.cxx_version }}" -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + run: > + cmake --preset="${{ matrix.configurePreset }}" + -DCCMATH_DESIRED_CXX_STANDARD="cxx_std_${{ matrix.cxx_version }}" + -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON + -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + -DCCMATH_BUILD_SIMPLE_TESTS=ON + -DCCMATH_BUILD_RIGOROUS_TESTS=OFF + -DCCMATH_ENABLE_MPFR_TESTS=OFF + -DCCMATH_ENABLE_POW_SEARCH_TOOLS=OFF - name: Build run: cmake --build --preset="${{ matrix.buildPreset }}" - name: Test - run: ctest --preset="${{ matrix.testPreset }}" --output-on-failure --schedule-random -F + run: ctest --preset="${{ matrix.testPreset }}" --output-on-failure --schedule-random -L simple -F + + rigorous: + needs: build-cxx17 + runs-on: macos-latest + timeout-minutes: 45 + + steps: + - name: Harden Runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Cache rigorous build + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: build-rigorous + key: ${{ runner.os }}-rigorous-build-${{ hashFiles('CMakeLists.txt', 'cmake/**', 'tests/**', 'include/**', 'thirdparty/**') }} + restore-keys: | + ${{ runner.os }}-rigorous-build- + + - name: Cache Homebrew downloads + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: | + ~/Library/Caches/Homebrew + key: ${{ runner.os }}-brew-mpfr-gmp-ninja-pkgconfig-v1 + + - name: Cache CORE-MATH prefix + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: coremath-prefix + key: ${{ runner.os }}-coremath-prefix-v1-${{ hashFiles('cmake/coremath/**', 'tools/coremath/**') }} + + - name: Install dependencies + run: brew install mpfr gmp + + - name: Build CORE-MATH oracle prefix + run: | + if [[ ! -f coremath-prefix/include/crmath.h ]]; then + bash tools/coremath/build_prefix.sh "${GITHUB_WORKSPACE}/coremath-prefix" + fi + + - name: Configure rigorous validation + run: > + cmake -S . -B build-rigorous -G Ninja + -DCCMATH_BUILD_TESTS=ON + -DCCMATH_BUILD_SIMPLE_TESTS=OFF + -DCCMATH_BUILD_RIGOROUS_TESTS=ON + -DCCMATH_ENABLE_MPFR_TESTS=ON + -DCCMATH_ENABLE_COREMATH_TESTS=ON + -DCCMATH_COREMATH_ROOT=${{ github.workspace }}/coremath-prefix + -DCCMATH_ENABLE_POW_SEARCH_TOOLS=ON + -DCCMATH_DESIRED_CXX_STANDARD=cxx_std_20 + -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON + -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + + - name: Build rigorous validation + run: cmake --build build-rigorous --parallel + + - name: Run rigorous validation + run: ctest --test-dir build-rigorous --output-on-failure -L rigorous diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml index 5c7d36b8..8ad35271 100644 --- a/.github/workflows/ci-windows.yml +++ b/.github/workflows/ci-windows.yml @@ -5,19 +5,20 @@ on: tags-ignore: - v*.* branches: - - '*' + - '**' pull_request: branches: - - '*' + - '**' permissions: contents: read jobs: - build: - runs-on: windows-latest + build-cxx17: + runs-on: windows-2022 + timeout-minutes: 45 strategy: - fail-fast: false + fail-fast: true matrix: include: - name: vs22-debug-cxx17 @@ -26,18 +27,62 @@ jobs: testPreset: test-vs22-debug cxx_version: 17 - - name: vs22-debug-cxx20 - configurePreset: vs22-debug - buildPreset: build-vs22-debug - testPreset: test-vs22-debug - cxx_version: 20 - - name: vs22-release-cxx17 configurePreset: vs22-release buildPreset: build-vs22-release testPreset: test-vs22-release cxx_version: 17 + steps: + - name: Harden Runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Cache build + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: | + ./**/CMakeFiles + ./**/CMakeCache.txt + ./**/out + key: ${{ runner.os }}-${{ env.ImageVersion }}-${{ matrix.name }}-${{ hashFiles('CMakeLists.txt', 'CMakePresets.json', 'cmake/**', 'include/**', 'tests/**', 'thirdparty/**') }} + restore-keys: | + ${{ runner.os }}-${{ env.ImageVersion }}-${{ matrix.name }}- + + - name: Configure + run: > + cmake --preset="${{ matrix.configurePreset }}" + -DCCMATH_DESIRED_CXX_STANDARD="cxx_std_${{ matrix.cxx_version }}" + -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON + -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + -DCCMATH_BUILD_SIMPLE_TESTS=ON + -DCCMATH_BUILD_RIGOROUS_TESTS=OFF + -DCCMATH_ENABLE_MPFR_TESTS=OFF + -DCCMATH_ENABLE_POW_SEARCH_TOOLS=OFF + + - name: Build + run: cmake --build --preset="${{ matrix.buildPreset }}" + + - name: Test + run: ctest --preset="${{ matrix.testPreset }}" --output-on-failure --schedule-random -L simple -F + + build-cxx20: + needs: build-cxx17 + runs-on: windows-2022 + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + include: + - name: vs22-debug-cxx20 + configurePreset: vs22-debug + buildPreset: build-vs22-debug + testPreset: test-vs22-debug + cxx_version: 20 + - name: vs22-release-cxx20 configurePreset: vs22-release buildPreset: build-vs22-release @@ -46,26 +91,125 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@v2.16.0 + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: egress-policy: audit - - uses: actions/checkout@v4 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Cache build - uses: actions/cache@v4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | ./**/CMakeFiles ./**/CMakeCache.txt ./**/out - key: ${{ runner.os }}-${{ matrix.name }} + key: ${{ runner.os }}-${{ env.ImageVersion }}-${{ matrix.name }}-${{ hashFiles('CMakeLists.txt', 'CMakePresets.json', 'cmake/**', 'include/**', 'tests/**', 'thirdparty/**') }} + restore-keys: | + ${{ runner.os }}-${{ env.ImageVersion }}-${{ matrix.name }}- - name: Configure - run: cmake --preset="${{ matrix.configurePreset }}" -DCCMATH_DESIRED_CXX_STANDARD="cxx_std_${{ matrix.cxx_version }}" -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + run: > + cmake --preset="${{ matrix.configurePreset }}" + -DCCMATH_DESIRED_CXX_STANDARD="cxx_std_${{ matrix.cxx_version }}" + -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON + -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON + -DCCMATH_BUILD_SIMPLE_TESTS=ON + -DCCMATH_BUILD_RIGOROUS_TESTS=OFF + -DCCMATH_ENABLE_MPFR_TESTS=OFF + -DCCMATH_ENABLE_POW_SEARCH_TOOLS=OFF - name: Build run: cmake --build --preset="${{ matrix.buildPreset }}" - name: Test - run: ctest --preset="${{ matrix.testPreset }}" --output-on-failure --schedule-random -F + run: ctest --preset="${{ matrix.testPreset }}" --output-on-failure --schedule-random -L simple -F + + rigorous: + needs: build-cxx20 + runs-on: windows-2022 + timeout-minutes: 90 + env: + CCMATH_VCPKG_BINARY_CACHE_DIR: ${{ github.workspace }}/.cache/vcpkg/archives + CCMATH_VCPKG_DOWNLOADS_DIR: ${{ github.workspace }}/.cache/vcpkg/downloads + + steps: + - name: Harden Runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Cache vcpkg downloads + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ${{ env.CCMATH_VCPKG_DOWNLOADS_DIR }} + key: ${{ runner.os }}-vcpkg-downloads-${{ hashFiles('cmake/vcpkg/vcpkg.json') }} + restore-keys: | + ${{ runner.os }}-vcpkg-downloads- + + - name: Cache vcpkg installed tree + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ${{ github.workspace }}/cmake/vcpkg/vcpkg_installed + key: ${{ runner.os }}-vcpkg-installed-${{ hashFiles('cmake/vcpkg/vcpkg.json') }} + restore-keys: | + ${{ runner.os }}-vcpkg-installed- + + - name: Cache vcpkg binary archives + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ${{ env.CCMATH_VCPKG_BINARY_CACHE_DIR }} + key: ${{ runner.os }}-vcpkg-binary-${{ hashFiles('cmake/vcpkg/vcpkg.json') }} + restore-keys: | + ${{ runner.os }}-vcpkg-binary- + + - name: Bootstrap vcpkg + shell: pwsh + run: | + if (-not (Test-Path vcpkg)) { + git clone --depth 1 https://github.com/microsoft/vcpkg.git vcpkg + # Fetch the exact builtin-baseline commit so manifest version resolution can read + # versions/baseline.json from it; a depth-1 clone of HEAD alone does not contain it. + git -C vcpkg fetch --depth 1 origin 10ceb139a610ebf3c6aa49cdc4a4b7f3db5d3f2b + git -C vcpkg checkout 10ceb139a610ebf3c6aa49cdc4a4b7f3db5d3f2b + } + if (-not (Test-Path vcpkg/vcpkg.exe)) { + .\vcpkg\bootstrap-vcpkg.bat -disableMetrics + } + New-Item -ItemType Directory -Force -Path "${{ env.CCMATH_VCPKG_DOWNLOADS_DIR }}" | Out-Null + New-Item -ItemType Directory -Force -Path "${{ env.CCMATH_VCPKG_BINARY_CACHE_DIR }}" | Out-Null + echo "VCPKG_ROOT=$((Get-Location).Path)\vcpkg" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + echo "VCPKG_DOWNLOADS=${{ env.CCMATH_VCPKG_DOWNLOADS_DIR }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + echo "VCPKG_DEFAULT_BINARY_CACHE=${{ env.CCMATH_VCPKG_BINARY_CACHE_DIR }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + echo "VCPKG_BINARY_SOURCES=clear;files,${{ env.CCMATH_VCPKG_BINARY_CACHE_DIR }},readwrite" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Cache rigorous build + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: out/build/vs22/vs22-release-rigorous + key: ${{ runner.os }}-${{ env.ImageVersion }}-vs22-release-rigorous-${{ hashFiles('CMakeLists.txt', 'cmake/**', 'tests/**', 'include/**', 'thirdparty/**') }} + restore-keys: | + ${{ runner.os }}-${{ env.ImageVersion }}-vs22-release-rigorous- + + - name: Configure rigorous validation + shell: pwsh + run: | + cmake --preset=vs22-release-rigorous ` + -DCCMATH_DESIRED_CXX_STANDARD=cxx_std_20 ` + -DCCMATH_ENABLE_AGGRESSIVE_WARNINGS=ON ` + -DCCMATH_ENABLE_WARNINGS_AS_ERRORS=ON ` + -DCCMATH_BUILD_SIMPLE_TESTS=OFF ` + -DCCMATH_BUILD_RIGOROUS_TESTS=ON ` + -DCCMATH_ENABLE_MPFR_TESTS=ON ` + -DCCMATH_ENABLE_POW_SEARCH_TOOLS=ON ` + -DCCMATH_ENABLE_COREMATH_TESTS=OFF ` + -DVCPKG_MANIFEST_DIR="${{ github.workspace }}/cmake/vcpkg" ` + -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" + + - name: Build rigorous validation + run: cmake --build --preset=build-vs22-release-rigorous --config Release + + - name: Run rigorous MPFR validation + run: ctest --preset=test-vs22-release-rigorous -C Release --output-on-failure -L rigorous diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a9cf6cc3..60cf3c81 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -30,16 +30,16 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@v2.16.0 + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: egress-policy: audit - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/init@dd903d2e4f5405488e5ef1422510ee31c8b32357 # v3.36.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -49,7 +49,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/autobuild@dd903d2e4f5405488e5ef1422510ee31c8b32357 # v3.36.2 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -62,6 +62,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/analyze@dd903d2e4f5405488e5ef1422510ee31c8b32357 # v3.36.2 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml deleted file mode 100644 index facdc26a..00000000 --- a/.github/workflows/dependency-review.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Dependency Review Action -# -# This Action will scan dependency manifest files that change as part of a Pull Request, -# surfacing known-vulnerable versions of the packages declared or updated in the PR. -# Once installed, if the workflow run is marked as required, -# PRs introducing known-vulnerable packages will be blocked from merging. -# -# Source repository: https://github.com/actions/dependency-review-action -name: 'Dependency Review' -on: [pull_request] - -permissions: - contents: read - -jobs: - dependency-review: - runs-on: ubuntu-latest - steps: - - name: Harden Runner - uses: step-security/harden-runner@v2.16.0 - with: - egress-policy: audit - - - name: 'Checkout Repository' - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: 'Dependency Review' - uses: actions/dependency-review-action@4081bf99e2866ebe428fc0477b69eb4fcda7220a # v4.4.0 diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index ba9b0605..443a70ef 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -5,34 +5,132 @@ on: tags-ignore: - v*.* branches: - - '*' + - '**' pull_request: branches: - - '*' + - '**' + schedule: + # Nightly deep campaign (corpus-accumulating, artifact-uploading). + - cron: '0 5 * * *' + workflow_dispatch: permissions: contents: read +env: + # Bounded smoke budget per target. The nightly campaign runs far longer. + CCMATH_FUZZ_SMOKE_RUNS: 30000 + jobs: + # Bounded, deterministic regression gate. Runs both build flavors: + # - default: public dispatch (libm-backed transcendentals + complete kernels) + # - generic: the in-house kernels directly, the high-value differential target libfuzzer-smoke: - runs-on: macos-latest + strategy: + fail-fast: false + matrix: + os: [macos-latest, ubuntu-latest] + fuzz_mode: [default, generic] + + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - name: Install dependencies + - name: Install dependencies (macOS) + if: runner.os == 'macOS' run: brew install ninja llvm lld - - name: Configure fuzz targets + - name: Install dependencies (Linux) + if: runner.os == 'Linux' + run: > + sudo apt-get update -qq && + sudo apt-get install -yqq --no-install-recommends + cmake ninja-build git llvm-18 clang-18 lld-18 libclang-rt-18-dev + + - name: Configure fuzz targets (macOS) + if: runner.os == 'macOS' run: | LLVM_PREFIX="$(brew --prefix llvm)" cmake --preset=ninja-fuzz-debug \ -DCMAKE_C_COMPILER="${LLVM_PREFIX}/bin/clang" \ -DCMAKE_CXX_COMPILER="${LLVM_PREFIX}/bin/clang++" \ - -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" + -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" \ + -DCCMATH_FUZZ_GENERIC=${{ matrix.fuzz_mode == 'generic' && 'ON' || 'OFF' }} + + - name: Configure fuzz targets (Linux) + if: runner.os == 'Linux' + run: | + cmake --preset=ninja-fuzz-debug \ + -DCMAKE_C_COMPILER=/usr/lib/llvm-18/bin/clang \ + -DCMAKE_CXX_COMPILER=/usr/lib/llvm-18/bin/clang++ \ + -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld-18" \ + -DCCMATH_FUZZ_GENERIC=${{ matrix.fuzz_mode == 'generic' && 'ON' || 'OFF' }} - name: Build fuzz targets run: cmake --build --preset=build-ninja-fuzz-debug - name: Fuzz smoke tests run: ctest --preset=test-ninja-fuzz-debug -C Debug --output-on-failure + + - name: Upload crash artifacts + if: failure() + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: fuzz-crashes-${{ matrix.os }}-${{ matrix.fuzz_mode }} + path: out/build/ninja/ninja-fuzz-debug/fuzzing/artifacts/**/crash-* + if-no-files-found: ignore + + # Nightly discovery: long campaigns with a persistent corpus. Non-blocking, since this is + # where new kernel bugs surface; reproducers are uploaded for triage. + libfuzzer-campaign: + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + strategy: + fail-fast: false + matrix: + fuzz_mode: [default, generic] + + runs-on: ubuntu-latest + continue-on-error: true + env: + CCMATH_FUZZ_MAX_TOTAL_TIME: 120 + CCMATH_FUZZ_KEEP_CORPUS: 1 + + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Install dependencies + run: > + sudo apt-get update -qq && + sudo apt-get install -yqq --no-install-recommends + cmake ninja-build git llvm-18 clang-18 lld-18 libclang-rt-18-dev + + - name: Restore fuzz corpus + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: out/build/ninja/ninja-fuzz-debug/fuzzing/corpus + key: fuzz-corpus-${{ matrix.fuzz_mode }}-${{ github.run_id }} + restore-keys: | + fuzz-corpus-${{ matrix.fuzz_mode }}- + + - name: Configure fuzz targets + run: | + cmake --preset=ninja-fuzz-debug \ + -DCMAKE_C_COMPILER=/usr/lib/llvm-18/bin/clang \ + -DCMAKE_CXX_COMPILER=/usr/lib/llvm-18/bin/clang++ \ + -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld-18" \ + -DCCMATH_FUZZ_GENERIC=${{ matrix.fuzz_mode == 'generic' && 'ON' || 'OFF' }} + + - name: Build fuzz targets + run: cmake --build --preset=build-ninja-fuzz-debug + + - name: Run fuzz campaign + run: cmake --build --preset=build-ninja-fuzz-debug --target ccmath-fuzz-campaign-all + + - name: Upload crash artifacts + if: failure() + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: fuzz-campaign-crashes-${{ matrix.fuzz_mode }} + path: out/build/ninja/ninja-fuzz-debug/fuzzing/artifacts/**/crash-* + if-no-files-found: ignore diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2da0576a..e92f2827 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,25 +1,85 @@ name: lint on: + push: + tags-ignore: + - v*.* + branches: + - '**' + paths: + - '.clang-format' + - '.github/actions/install-clang-format/**' + - '.github/workflows/lint.yml' + - 'include/**/*.hpp' + - 'tools/apply_format.sh' + - 'tools/clang_format_version' + - 'tools/ensure_format.sh' pull_request: + branches: + - '**' paths: - - '**.hpp' + - '.clang-format' + - '.github/actions/install-clang-format/**' + - '.github/workflows/lint.yml' + - 'include/**/*.hpp' + - 'tools/apply_format.sh' + - 'tools/clang_format_version' + - 'tools/ensure_format.sh' -permissions: - contents: read +concurrency: + group: lint-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: - format_code: + format_check: + if: github.event_name == 'pull_request' + permissions: + contents: read runs-on: ubuntu-latest steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - uses: ./.github/actions/install-clang-format + + - name: Check clang-format + run: bash tools/ensure_format.sh - - name: Install clang-format - uses: aminya/setup-cpp@3aac37c3fbde6cb1d2fa0a0a7e945b63b10561d5 # v1.1.0 + format_autofix: + if: github.event_name == 'push' + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: - clangformat: 17.0.5 + ref: ${{ github.ref_name }} + fetch-depth: 0 - - name: Run clang-format + - uses: ./.github/actions/install-clang-format + + - name: Check clang-format + id: check_format + run: bash tools/ensure_format.sh + continue-on-error: true + + - name: Apply clang-format + if: steps.check_format.outcome == 'failure' + run: bash tools/apply_format.sh + + - name: Commit clang-format fixes + if: steps.check_format.outcome == 'failure' run: | - find include -name '*.hpp' | xargs clang-format -i -style=file -fallback-style=none - git diff --exit-code + if git diff --quiet -- include; then + echo "No formatting changes to commit." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add include + git commit -m "ci: apply clang-format" + git push origin HEAD:${{ github.ref_name }} + + - name: Verify clang-format after auto-fix + if: steps.check_format.outcome == 'failure' + run: bash tools/ensure_format.sh diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 7003d8e3..1aab3f24 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -32,18 +32,18 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@v2.16.0 + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: egress-policy: audit - name: "Checkout code" - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif @@ -62,7 +62,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: SARIF file @@ -72,6 +72,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/upload-sarif@dd903d2e4f5405488e5ef1422510ee31c8b32357 # v3.36.2 with: sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index 24dd2b7e..4abc396a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,12 +3,48 @@ **/.vscode/* **/.idea/* .idea/* +!.idea/cmake.xml .vs/* .vscode/* build/* +/build-*/ /out/ +mpfr-pow*-summary.json +mpfr-powf*-summary.json +mpfr-powl-summary.json +coremath-pow*-summary.json +coremath-powf*-summary.json +coremath-prefix/ +cmake/vcpkg/vcpkg_installed/ +vcpkg_installed/ +pow-search-results.json +powl-platform.json +tests/rigorous/oracle_logs/ +showcase/reports/ +showcase/compile_time/probes/ +path-matrix-json*/ .cache + +# LaTeX build artifacts (.tex and .bib remain tracked) +*.aux +*.bbl +*.blg +*.fdb_latexmk +*.fls +*.synctex.gz +*.toc +docs/proofs/**/*.pdf +tools/proofs/**/build/ + +# Sollya approximating_functions regenerated outputs (see docs/approximating_functions/README.md) +docs/approximating_functions/logs/ +docs/approximating_functions/data/ + +# Local duplicate approximation tree (canonical: docs/approximating_functions/APPROXIMATING_FUNCTIONS.pdf) +docs/approximation_guide/ .DS_Store +__pycache__/ +*.py[cod] # User Specific Configuration Files CMakeSettings.json @@ -70,3 +106,17 @@ fuzzing/oom-* fuzzing/slow-unit-* fuzzing/minimize-crash-* +# asmlab cached reference data (Agner Fog tables, uops.info exports); fetched on demand +tools/asmlab/refdata/* +!tools/asmlab/refdata/.gitkeep + +# asmlab rendered harness TUs (from tools/asmlab/**/*.cpp.in); primary output is under /out/ +tools/asmlab/harness/harness.cpp +tools/asmlab/harness/constexpr_probe.cpp +tools/asmlab/golden/harness.cpp + +# Premake generated artifacts at the repository root +/Makefile +/bin/ +/obj/ + diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b4f0da7..ab164801 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.18.0) # Project Setup and Metadata # ---------------------------------------------------------------------- set(CCMATH_TARGET_NAME "ccmath") -set(CCMATH_BUILD_VERSION 0.2.0) +set(CCMATH_BUILD_VERSION 0.3.0) project( ${CCMATH_TARGET_NAME} @@ -39,6 +39,13 @@ message(STATUS "CCMath: Version: ${CCMATH_BUILD_VERSION}") # Load user-configurable options (e.g., BUILD_EXAMPLES, BUILD_TESTS, etc.) include(cmake/config/UserOptions.cmake) +# Deterministic mode requires every call to route through the generic kernels, so +# runtime SIMD (a separate, non-generic evaluation path) must be off regardless of +# how CCMATH_DISABLE_RUNTIME_SIMD was set. +if (CCMATH_ENABLE_DETERMINISTIC) + set(CCMATH_DISABLE_RUNTIME_SIMD ON CACHE BOOL "Disabled by deterministic mode" FORCE) +endif () + if (CCMATH_DISABLE_RUNTIME_SIMD) set(CCMATH_ENABLE_RUNTIME_SIMD OFF) else () @@ -127,6 +134,11 @@ set(CCMATH_VERSION_MINOR ${CMAKE_PROJECT_VERSION_MINOR}) set(CCMATH_VERSION_PATCH ${CMAKE_PROJECT_VERSION_PATCH}) configure_file("${PROJECT_SOURCE_DIR}/cmake/in/version.hpp.in" "${PROJECT_BINARY_DIR}/include/ccmath/version.hpp") +if (CCMATH_PROJECT_IS_TOP_LEVEL) + include(cmake/config/EmitBuildManifest.cmake) + ccmath_setup_secondary_build_generation() +endif () + # ---------------------------------------------------------------------- # Installation Rules @@ -140,7 +152,7 @@ endif () # Subdirectories for Examples, Benchmarks, and Tests # ---------------------------------------------------------------------- # Only add these if the user explicitly asked for them in the options. -if (CCMATH_BUILD_EXAMPLES OR CCMATH_BUILD_BENCHMARKS OR CCMATH_BUILD_TESTS) +if (CCMATH_BUILD_EXAMPLES OR CCMATH_BUILD_BENCHMARKS OR CCMATH_BUILD_TESTS OR CCMATH_BUILD_SHOWCASE) # Use SYSTEM to suppress warnings from external code if supported by cmake if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.25) add_subdirectory(thirdparty SYSTEM) @@ -175,6 +187,15 @@ if (CCMATH_BUILD_EXAMPLES OR CCMATH_BUILD_BENCHMARKS OR CCMATH_BUILD_TESTS) else () add_subdirectory(tests) endif () + include(cmake/config/TestCtestTargets.cmake) + endif () + + if (CCMATH_BUILD_SHOWCASE) + if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.25) + add_subdirectory(showcase SYSTEM) + else () + add_subdirectory(showcase) + endif () endif () endif () diff --git a/CMakePresets.json b/CMakePresets.json index bf5a3fb1..2f6b4f09 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,5 +1,5 @@ { - "version": 2, + "version": 6, "cmakeMinimumRequired": { "major": 3, "minor": 18, @@ -19,6 +19,16 @@ "CMAKE_CXX_STANDARD": "17" } }, + { + "name": "base-clion", + "description": "Single-config Ninja for CLion", + "generator": "Ninja", + "hidden": true, + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "CMAKE_CXX_STANDARD": "17" + } + }, { "name": "base-vs19", "description": "Base configuration using Visual Studio 16 (2019)", @@ -145,6 +155,62 @@ "CMAKE_CXX_FLAGS_DEBUG": "-O1" } }, + { + "name": "rigorous", + "hidden": true, + "cacheVariables": { + "CCMATH_BUILD_TESTS": "ON", + "CCMATH_BUILD_SIMPLE_TESTS": "OFF", + "CCMATH_BUILD_RIGOROUS_TESTS": "ON", + "CCMATH_ENABLE_MPFR_TESTS": "ON", + "CCMATH_ENABLE_POW_SEARCH_TOOLS": "ON", + "CCMATH_DESIRED_CXX_STANDARD": "cxx_std_20", + "CCMATH_ENABLE_AGGRESSIVE_WARNINGS": "ON", + "CCMATH_ENABLE_WARNINGS_AS_ERRORS": "ON" + } + }, + { + "name": "rigorous-coremath", + "hidden": true, + "cacheVariables": { + "CCMATH_ENABLE_COREMATH_TESTS": "ON", + "CCMATH_COREMATH_ROOT": "${sourceDir}/coremath-prefix" + } + }, + { + "name": "showcase", + "hidden": true, + "cacheVariables": { + "CCMATH_BUILD_SHOWCASE": "ON", + "CCMATH_BUILD_BENCHMARKS": "ON", + "CCMATH_BUILD_TESTS": "OFF", + "CCMATH_SHOWCASE_ENABLE_GCEM": "ON", + "CCMATH_SHOWCASE_ENABLE_MPFR": "ON", + "CCMATH_DESIRED_CXX_STANDARD": "cxx_std_20" + } + }, + { + "name": "examples", + "hidden": true, + "cacheVariables": { + "CCMATH_BUILD_EXAMPLES": "ON", + "CCMATH_BUILD_TESTS": "OFF", + "CCMATH_BUILD_BENCHMARKS": "OFF", + "CCMATH_BUILD_SHOWCASE": "OFF" + } + }, + { + "name": "rigorous-vcpkg", + "hidden": true, + "environment": { + "VCPKG_ROOT": "${sourceDir}/vcpkg" + }, + "cacheVariables": { + "CCMATH_ENABLE_COREMATH_TESTS": "OFF", + "VCPKG_MANIFEST_DIR": "${sourceDir}/cmake/vcpkg", + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + }, { "name": "ninja-gcc-debug", "inherits": [ @@ -182,6 +248,86 @@ ], "description": "Ninja Multi-config + Clang (Debug)" }, + { + "name": "clion-clang-debug", + "displayName": "CLion Clang Debug", + "inherits": [ + "base-clion", + "clang", + "Debug" + ], + "binaryDir": "${sourceDir}/out/clang", + "description": "Single-config Ninja + Clang (Debug) for CLion" + }, + { + "name": "clion-clang-debug-examples", + "displayName": "CLion Clang Debug + examples", + "inherits": [ + "base-clion", + "clang", + "Debug", + "examples" + ], + "binaryDir": "${sourceDir}/out/clang-examples", + "description": "Single-config Ninja + Clang (Debug) for CLion examples" + }, + { + "name": "clion-clang-debug-rigorous-dev", + "displayName": "CLion Clang Debug + rigorous", + "inherits": [ + "base-clion", + "clang", + "Debug", + "rigorous" + ], + "binaryDir": "${sourceDir}/out/clang-rigorous-dev", + "description": "CLion Clang Debug with simple and rigorous test suites", + "cacheVariables": { + "CCMATH_BUILD_SIMPLE_TESTS": "ON", + "CCMATH_ENABLE_AGGRESSIVE_WARNINGS": "OFF", + "CCMATH_ENABLE_WARNINGS_AS_ERRORS": "OFF", + "CCMATH_ENABLE_COREMATH_TESTS": "OFF" + } + }, + { + "name": "clion-clang-release-showcase", + "displayName": "CLion Clang Release + showcase", + "inherits": [ + "base-clion", + "clang", + "Release", + "showcase" + ], + "binaryDir": "${sourceDir}/out/clang-showcase", + "description": "Single-config Release showcase comparisons for CLion" + }, + { + "name": "ninja-clang-debug-rigorous-dev", + "inherits": [ + "base", + "clang", + "Debug", + "rigorous", + "rigorous-coremath" + ], + "description": "Clang Debug with simple and rigorous test suites for CLion", + "cacheVariables": { + "CCMATH_BUILD_SIMPLE_TESTS": "ON", + "CCMATH_ENABLE_AGGRESSIVE_WARNINGS": "OFF", + "CCMATH_ENABLE_WARNINGS_AS_ERRORS": "OFF" + } + }, + { + "name": "ninja-clang-debug-rigorous", + "inherits": [ + "base", + "clang", + "Debug", + "rigorous", + "rigorous-coremath" + ], + "description": "Clang Debug for rigorous validation only" + }, { "name": "ninja-fuzz-debug", "inherits": [ @@ -200,6 +346,38 @@ ], "description": "Ninja Multi-config + Clang (Release)" }, + { + "name": "ninja-clang-release-rigorous", + "inherits": [ + "base", + "clang", + "Release", + "rigorous", + "rigorous-coremath" + ], + "description": "Ninja Multi-config + Clang (Release) for rigorous validation" + }, + { + "name": "ninja-clang-release-showcase", + "inherits": [ + "base", + "clang", + "Release", + "showcase" + ], + "description": "Ninja Multi-config + Clang (Release) for library showcase comparisons" + }, + { + "name": "ninja-gcc-release-rigorous", + "inherits": [ + "base", + "gcc", + "Release", + "rigorous", + "rigorous-coremath" + ], + "description": "Ninja Multi-config + GCC (Release) for rigorous validation" + }, { "name": "ninja-clang-minsizerel", "inherits": [ @@ -259,6 +437,17 @@ ], "description": "VS2022 + Release" }, + { + "name": "vs22-release-rigorous", + "inherits": [ + "base-vs22", + "Release", + "rigorous", + "rigorous-vcpkg" + ], + "description": "VS2022 + Release for rigorous MPFR validation via vcpkg", + "binaryDir": "${sourceDir}/out/build/vs22/vs22-release-rigorous" + }, { "name": "vs22-clang-debug", "inherits": [ @@ -302,6 +491,52 @@ "all" ] }, + { + "name": "build-clion-clang-debug", + "configurePreset": "clion-clang-debug", + "targets": [ + "all" + ] + }, + { + "name": "build-clion-clang-debug-examples", + "configurePreset": "clion-clang-debug-examples", + "targets": [ + "ccmath-example-all" + ] + }, + { + "name": "build-clion-clang-debug-rigorous-dev", + "configurePreset": "clion-clang-debug-rigorous-dev", + "targets": [ + "all" + ] + }, + { + "name": "build-clion-clang-release-showcase", + "configurePreset": "clion-clang-release-showcase", + "targets": [ + "showcase_bench_sqrt", + "showcase_bench_sin", + "showcase_accuracy" + ] + }, + { + "name": "build-ninja-clang-debug-rigorous-dev", + "configurePreset": "ninja-clang-debug-rigorous-dev", + "configuration": "Debug", + "targets": [ + "all" + ] + }, + { + "name": "build-ninja-clang-debug-rigorous", + "configurePreset": "ninja-clang-debug-rigorous", + "configuration": "Debug", + "targets": [ + "all" + ] + }, { "name": "build-ninja-fuzz-debug", "configurePreset": "ninja-fuzz-debug", @@ -318,6 +553,32 @@ "all" ] }, + { + "name": "build-ninja-clang-release-rigorous", + "configurePreset": "ninja-clang-release-rigorous", + "configuration": "Release", + "targets": [ + "all" + ] + }, + { + "name": "build-ninja-clang-release-showcase", + "configurePreset": "ninja-clang-release-showcase", + "configuration": "Release", + "targets": [ + "showcase_bench_sqrt", + "showcase_bench_sin", + "showcase_accuracy" + ] + }, + { + "name": "build-ninja-gcc-release-rigorous", + "configurePreset": "ninja-gcc-release-rigorous", + "configuration": "Release", + "targets": [ + "all" + ] + }, { "name": "build-ninja-clang-minsizerel", "configurePreset": "ninja-clang-minsizerel", @@ -374,6 +635,14 @@ "ALL_BUILD" ] }, + { + "name": "build-vs22-release-rigorous", + "configurePreset": "vs22-release-rigorous", + "configuration": "Release", + "targets": [ + "ALL_BUILD" + ] + }, { "name": "build-vs22-clang-debug", "configurePreset": "vs22-clang-debug", @@ -408,6 +677,55 @@ "configuration": "Debug", "inheritConfigureEnvironment": true }, + { + "name": "test-clion-clang-debug", + "configurePreset": "clion-clang-debug", + "inheritConfigureEnvironment": true, + "output": { + "outputOnFailure": true + }, + "filter": { + "include": { + "label": "simple" + } + } + }, + { + "name": "test-clion-clang-debug-rigorous-dev", + "configurePreset": "clion-clang-debug-rigorous-dev", + "inheritConfigureEnvironment": true, + "output": { + "outputOnFailure": true + }, + "filter": { + "include": { + "label": "rigorous" + } + } + }, + { + "name": "test-ninja-clang-debug-rigorous-dev", + "configurePreset": "ninja-clang-debug-rigorous-dev", + "configuration": "Debug", + "inheritConfigureEnvironment": true, + "output": { + "outputOnFailure": true + } + }, + { + "name": "test-ninja-clang-debug-rigorous", + "configurePreset": "ninja-clang-debug-rigorous", + "configuration": "Debug", + "inheritConfigureEnvironment": true, + "output": { + "outputOnFailure": true + }, + "filter": { + "include": { + "label": "rigorous" + } + } + }, { "name": "test-ninja-fuzz-debug", "configurePreset": "ninja-fuzz-debug", @@ -425,6 +743,34 @@ "configuration": "Release", "inheritConfigureEnvironment": true }, + { + "name": "test-ninja-clang-release-rigorous", + "configurePreset": "ninja-clang-release-rigorous", + "configuration": "Release", + "inheritConfigureEnvironment": true, + "output": { + "outputOnFailure": true + }, + "filter": { + "include": { + "label": "rigorous" + } + } + }, + { + "name": "test-ninja-gcc-release-rigorous", + "configurePreset": "ninja-gcc-release-rigorous", + "configuration": "Release", + "inheritConfigureEnvironment": true, + "output": { + "outputOnFailure": true + }, + "filter": { + "include": { + "label": "rigorous" + } + } + }, { "name": "test-ninja-clang-minsizerel", "configurePreset": "ninja-clang-minsizerel", @@ -467,11 +813,152 @@ "configuration": "Release", "inheritConfigureEnvironment": true }, + { + "name": "test-vs22-release-rigorous", + "configurePreset": "vs22-release-rigorous", + "configuration": "Release", + "inheritConfigureEnvironment": true, + "output": { + "outputOnFailure": true + }, + "filter": { + "include": { + "label": "rigorous" + } + } + }, { "name": "test-vs22-clang-debug", "configurePreset": "vs22-clang-debug", "configuration": "Debug", "inheritConfigureEnvironment": true } + ], + "workflowPresets": [ + { + "name": "workflow-clion-clang-debug-examples", + "displayName": "CLion examples", + "description": "Configure and build the example targets for CLion", + "steps": [ + { + "type": "configure", + "name": "clion-clang-debug-examples" + }, + { + "type": "build", + "name": "build-clion-clang-debug-examples" + } + ] + }, + { + "name": "workflow-clion-clang-debug-rigorous-dev", + "displayName": "CLion rigorous validation", + "description": "Configure, build, and run rigorous ctest for CLion", + "steps": [ + { + "type": "configure", + "name": "clion-clang-debug-rigorous-dev" + }, + { + "type": "build", + "name": "build-clion-clang-debug-rigorous-dev" + }, + { + "type": "test", + "name": "test-clion-clang-debug-rigorous-dev" + } + ] + }, + { + "name": "workflow-clion-clang-release-showcase", + "displayName": "CLion showcase", + "description": "Configure and build showcase comparison targets for CLion", + "steps": [ + { + "type": "configure", + "name": "clion-clang-release-showcase" + }, + { + "type": "build", + "name": "build-clion-clang-release-showcase" + } + ] + }, + { + "name": "workflow-ninja-clang-debug-rigorous-dev", + "displayName": "Dev + rigorous (Clang Debug)", + "description": "Configure, build, and run all ctest suites for CLion development", + "steps": [ + { + "type": "configure", + "name": "ninja-clang-debug-rigorous-dev" + }, + { + "type": "build", + "name": "build-ninja-clang-debug-rigorous-dev" + }, + { + "type": "test", + "name": "test-ninja-clang-debug-rigorous-dev" + } + ] + }, + { + "name": "workflow-ninja-clang-release-rigorous", + "displayName": "Rigorous validation (Clang Release)", + "description": "Configure, build, and run the rigorous ctest suite", + "steps": [ + { + "type": "configure", + "name": "ninja-clang-release-rigorous" + }, + { + "type": "build", + "name": "build-ninja-clang-release-rigorous" + }, + { + "type": "test", + "name": "test-ninja-clang-release-rigorous" + } + ] + }, + { + "name": "workflow-ninja-gcc-release-rigorous", + "displayName": "Rigorous validation (GCC Release)", + "description": "Configure, build, and run the rigorous ctest suite", + "steps": [ + { + "type": "configure", + "name": "ninja-gcc-release-rigorous" + }, + { + "type": "build", + "name": "build-ninja-gcc-release-rigorous" + }, + { + "type": "test", + "name": "test-ninja-gcc-release-rigorous" + } + ] + }, + { + "name": "workflow-vs22-release-rigorous", + "displayName": "Rigorous validation (VS2022 Release)", + "description": "Configure, build, and run the rigorous MPFR ctest suite via vcpkg", + "steps": [ + { + "type": "configure", + "name": "vs22-release-rigorous" + }, + { + "type": "build", + "name": "build-vs22-release-rigorous" + }, + { + "type": "test", + "name": "test-vs22-release-rigorous" + } + ] + } ] } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 640fb772..ac5c38a0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,225 +1,90 @@ -# Contribution Guidelines - -> [!IMPORTANT] -> Currently the contribution guidelines are a work in progress. Please check back later for more information. - # Contributing to CCMath -First off, thanks for taking the time to contribute! ❤️ - -All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 - -If you enjoy this project but don’t have the time or ability to contribute code, you can still show your support in the following ways: -- Star the repository -- Mention this project in your own project's documentation -- Share this project on social media or at community meetups - - -## Table of Contents - -- [I Have a Question](#i-have-a-question) -- [I Want To Contribute](#i-want-to-contribute) - - [Core Tenets of CCMath](#core-tenets-of-ccmath) - - [Your First Code Contribution](#your-first-code-contribution) -- [How to manually build CCMath with the command line](#how-to-manually-build-ccmath-with-the-command-line) - - [Prerequisites](#prerequisites) - - [How to clone project from GitHub](#how-to-clone-project-from-github) - - [How to build the project](#how-to-build-the-project) - - [How to run tests](#how-to-run-tests) -- [Additional Notes](#additional-notes) - - -## I Have a Question - -> If you want to ask a question, we assume that you have read the available [Documentation](). - -Before you ask a question, it is best to search for existing [Issues](https://github.com/Rinzii/ccmath/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first. - -If you then still feel the need to ask a question and need clarification, we recommend the following: - -- Open an [Issue](https://github.com/Rinzii/ccmath/issues/new). -- Provide as much context as you can about what you're running into. -- Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant. - -We will then take care of the issue as soon as possible. - - -## I Want To Contribute - -> **Legal Notice** -> By contributing to this project, you agree that: -> - You have authored 100% of the content, or the content is compatible with the project’s license and is marked as such -> - You have the rights necessary to share the content -> - The content may be provided under the project’s license - -***Before any code can be written, consider these core tenets of CCMath as you work on the project.*** - -### Core Tenets of CCMath +For help understanding how to implement elementary functions in CCMath, see +[Approximating Functions: A Practical Guide to Sollya and Function Implementation](docs/approximating_functions/APPROXIMATING_FUNCTIONS.pdf). -1. Code should be evaluable at compile time when possible and great effort should be taken to comply with this (e.g., `constexpr` and `static_assert`). -2. Match the behavior of the standard library as closely as possible, with no compromises on accuracy. -3. Strive for performance comparable to (or no worse than half the speed of) the standard library. -4. Ensure code can work with `static_assert`. -5. Favor generic solutions over platform-specific ones, when possible. -6. Mimic the behavior of compilers we support (particularly relevant for `ccm::fpclassify`). -7. Provide comprehensive tests covering all edge cases for any new code. -8. Document thoroughly with Doxygen, ensuring clarity and completeness. -9. Follow the same API layout as the standard library. Refer to [cppreference](https://en.cppreference.com/w/cpp/header/cmath) for guidance on `'s api. +PRs go to the `dev` branch. Bug reports and design questions belong in +[Issues](https://github.com/Rinzii/ccmath/issues) or +[Discord](https://discord.gg/p3mVxAbdmc). -### Your First Code Contribution +By contributing you confirm the change is yours to license and compatible with +Apache-2.0 WITH LLVM-exception. -If you’re ready to make your first contribution to CCMath, here’s a basic workflow you can follow: +## Core tenets -1. **Fork and Clone** - - Fork the CCMath repository on GitHub to your personal account. - - Clone your fork locally: - ```bash - git clone https://github.com//ccmath.git - cd ccmath - ``` +1. Prefer constexpr evaluation where the standard allows it (`gen` paths, `static_assert` smoke). +2. Match `` observable behavior. Accuracy targets are documented in tests, typically within 4 ULP of the platform libm unless noted. +3. Keep runtime performance in the same ballpark as the platform libm on covered paths. +4. Follow the existing dispatch layout (`builtin` / `gen` / `rt`, or impl-direct where the family already does). +5. Prefer portable generic kernels over platform forks when both satisfy the standard. +6. Mirror supported compiler behavior where the standard leaves details unspecified (for example `fpclassify` payload bits). +7. Add tests in the same change. Cover standards-mandated edge cases and ULP checks on public APIs. +8. Doxygen on exported entry points (`@brief`, `@param`, `@return`). +9. Keep the public header layout aligned with [cppreference ``](https://en.cppreference.com/w/cpp/header/cmath). -2. **Check Out the `dev` Branch** - - We recommend making changes off of the `dev` branch: - ```bash - git checkout dev - ``` +## First PR -3. **Set Up Your Environment** - - Make sure you have the prerequisites installed (CMake, Ninja, Git, and a compatible compiler). - - Install any other dependencies specific to your platform (for example, using `apt`, `brew`, or `choco`). - -4. **Create a Feature Branch** - - Create a new branch for your proposed changes: - ```bash - git checkout -b feature/my-new-feature - ``` - -5. **Implement Your Changes** - - Edit or add source/header files under `ccmath/`. - - Remember the [Core Tenets of CCMath](#core-tenets-of-ccmath)—aim for constexpr-friendly, standards-compliant, well-documented, and well-tested code. - -6. **Build and Test Locally** - - Use the provided CMake Presets to configure, build, and test: - ```bash - cmake --preset=ninja-gcc-debug - cmake --build --preset=build-ninja-gcc-debug - ctest --preset=test-ninja-gcc-debug --output-on-failure - ``` - - If you encounter any build or test failures, consult the project’s [Issues](https://github.com/Rinzii/ccmath/issues) or open a new one if needed. - -7. **Document Your Code** - - Add or update Doxygen comments where appropriate to ensure comprehensive coverage of new or modified functions. - - If applicable, include references or code examples in docstrings. - -8. **Commit and Push** - - Once your changes pass locally, commit them with a meaningful message: - ```bash - git add . - git commit -m "Implement new math function with tests" - ``` - - Push your branch to your fork: - ```bash - git push -u origin feature/my-new-feature - ``` +```bash +git clone https://github.com//ccmath.git +cd ccmath +git checkout dev +git checkout -b +``` -9. **Open a Pull Request** - - On GitHub, open a Pull Request (PR) from your branch to `Rinzii/ccmath`’s `dev` branch. - - Provide a clear title and description of your changes. - - Describe any related issue references (if applicable) and how you tested your changes. +Configure, build, and run the simple test preset: -10. **Code Review** -- A maintainer will review your PR. If changes are requested, address them and push updates to the same feature branch. -- Once everything is approved, your PR will be merged into `dev`. +```bash +cmake --preset=ninja-gcc-debug +cmake --build --preset=build-ninja-gcc-debug +ctest --preset=test-ninja-gcc-debug --output-on-failure +``` -That’s it! Thank you for contributing to CCMath. If you have any questions or run into any problems, don’t hesitate to open an issue or add a comment in your PR. We appreciate your help in making CCMath a robust, efficient, and user-friendly math library. +Commit with a one-line imperative subject (no trailing period). Open a PR against +`dev` with a short summary of what changed and how you tested it. +Implementation work should stay under `include/ccmath/` using the patterns already +present in the function family you touch. Do not add `` includes under +`include/`. -## How to manually build CCMath with the command line +## Build from the command line ### Prerequisites -- [CMake 3.18+](https://cmake.org/download/) -- [Git](https://git-scm.com/) -- [Ninja](https://ninja-build.org/) (for Linux & macOS -- [Clang](https://clang.llvm.org/) or [GCC](https://gcc.gnu.org/) (for Linux & macOS) -- [Visual Studio 22](https://visualstudio.microsoft.com/) (for Windows) -### How to clone project from GitHub +- CMake 3.18+ +- Git +- Ninja (Linux and macOS) +- Clang or GCC (Linux and macOS) +- Visual Studio 2022 (Windows) + +### Linux and macOS -Step 1: Clone the repository ```bash git clone https://github.com/Rinzii/ccmath.git +cd ccmath +git checkout dev +cmake --preset=ninja-gcc-debug +cmake --build --preset=build-ninja-gcc-debug +ctest --preset=test-ninja-gcc-debug --output-on-failure ``` -Step 2: (recommended) Check out the dev branch -```bash +Other configure presets are listed in `CMakePresets.json` (`ninja-clang-debug`, +`ninja-clang-release`, and similar). Pass `-DCMAKE_CXX_STANDARD=17` or `20` on the +configure line when you need a specific standard. + +### Windows (Visual Studio 2022) + +```powershell +git clone https://github.com/Rinzii/ccmath.git +cd ccmath git checkout dev +cmake --preset=vs22-debug +cmake --build --preset=build-vs22-debug +ctest --preset=test-vs22-debug --output-on-failure ``` -## Building on Linux & macOS - -1. Pick a preset for your compiler and configuration. - - Examples (see `CMakePresets.json` for the full list): - - `ninja-gcc-debug` - - `ninja-gcc-release` - - `ninja-clang-debug` - - `ninja-clang-release` - - `ninja-clang-libcpp-relwithdebinfo` - - If you need a specific C++ standard, pass `-DCMAKE_CXX_STANDARD=17` (or 20, etc.) after specifying the preset. -2. Configure using one of the presets (example shown uses GCC Debug with C++17): - ```bash - cmake --preset=ninja-gcc-debug -DCMAKE_CXX_STANDARD=17 - ``` -3. Build the project - ```bash - cmake --preset=ninja-gcc-debug -DCMAKE_CXX_STANDARD=17 - ``` - Or, if you prefer a single step after configuring: - ```bash - cmake --preset=ninja-gcc-debug -DCMAKE_CXX_STANDARD=17 - ``` - You can also specify `--config Debug` or `--config Release` if needed -4. Run the tests - ```bash - ctest --preset=test-ninja-gcc-debug --output-on-failure - ``` - Or, if you prefer a one-liner after configuring and building: - ```bash - ctest -C Debug --output-on-failure - ``` - (Ensure you built a Debug config if you’re testing Debug.) - -## Building on Windows (Visual Studio 2022) - -1. Choose a preset, for example: - - `vs22-debug` - - `vs22-release` - - `vs22-clang-debug` - -2. Configure: - ```powershell - cmake --preset=vs22-debug -DCMAKE_CXX_STANDARD=17 - ``` -3. Build using the corresponding build preset: - ```powershell - cmake --build --preset=vs22-debug - ``` - Or, if you prefer a single step after configuring: - ```powershell - # In the same directory where you ran cmake --preset=... - cmake --build . --config Debug - ``` -4. Run the tests - ```powershell - ctest --preset=test-vs22-debug --output-on-failure - ``` - Or, if you prefer a one-liner after configuring and building: - ```powershell - ctest -C Debug --output-on-failure - ``` - -## Additional Notes - -- The exact preset names may differ in your local `CMakePresets.json`. Check that file for the list of available presets. -- Presets for coverage, sanitizer builds, or specialized compilers like Intel may also be available (for example, `ninja-intel-relwithdebinfo` or `ninja-gcc-ccache-debug`). -- If you run into issues, confirm that your CMake, Ninja, and compiler versions are current and supported. -- See our [Contribution Guidelines](CONTRIBUTING.md) for more detailed information on contributing and troubleshooting. +Preset names vary. Read `CMakePresets.json` before assuming a name exists locally. + +## More detail + +- [docs/STATUS.md](docs/STATUS.md) for module completion +- [SECURITY.md](SECURITY.md) for vulnerability reports diff --git a/README.md b/README.md index 6e0b99d0..f22bbafa 100644 --- a/README.md +++ b/README.md @@ -1,124 +1,392 @@ CCMath Logo -# CCMath - A Modern, Compile-Time `` Alternative +# CCMath -[![image](https://github.com/Rinzii/ccmath/workflows/ci-windows/badge.svg)](https://github.com/Rinzii/ccmath/actions?query=workflow%3Aci-windows) -[![image](https://github.com/Rinzii/ccmath/workflows/ci-linux/badge.svg)](https://github.com/Rinzii/ccmath/actions?query=workflow%3Aci-linux) -[![image](https://github.com/Rinzii/ccmath/workflows/ci-macos/badge.svg)](https://github.com/Rinzii/ccmath/actions?query=workflow%3Aci-macos) -[![image](https://api.securityscorecards.dev/projects/github.com/Rinzii/ccmath/badge)](https://securityscorecards.dev/viewer/?uri=github.com/Rinzii/ccmath) +## A constexpr-first `` library for C++17 and later + +[![Windows CI](https://github.com/Rinzii/ccmath/actions/workflows/ci-windows.yml/badge.svg?branch=main)](https://github.com/Rinzii/ccmath/actions/workflows/ci-windows.yml?query=branch%3Amain) +[![Linux CI](https://github.com/Rinzii/ccmath/actions/workflows/ci-linux.yml/badge.svg?branch=main)](https://github.com/Rinzii/ccmath/actions/workflows/ci-linux.yml?query=branch%3Amain) +[![macOS CI](https://github.com/Rinzii/ccmath/actions/workflows/ci-macos.yml/badge.svg?branch=main)](https://github.com/Rinzii/ccmath/actions/workflows/ci-macos.yml?query=branch%3Amain) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/Rinzii/ccmath/badge)](https://securityscorecards.dev/viewer/?uri=github.com/Rinzii/ccmath) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9402/badge)](https://www.bestpractices.dev/projects/9402) -[![License](https://img.shields.io/badge/License-Apache%202.0%20WITH%20LLVM--exception-blue.svg)]( -https://opensource.org/licenses/Apache-2.0) +[![License](https://img.shields.io/badge/License-Apache%202.0%20WITH%20LLVM--exception-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Discord](https://img.shields.io/discord/1286067628456284244?label=Discord)](https://discord.gg/p3mVxAbdmc) -**CCMath** is a C++17 library designed to provide a reimplementation of the standard library, emphasizing -comprehensive constexpr support. - -## Key Features - -- **Full `constexpr` Compatibility**: Every function in CCMath is implemented as `constexpr`, ensuring compatibility - with `static_assert` and enabling robust compile-time evaluations. -- **Seamless `` Replacement**: CCMath replicates the functionality of the standard `` library, offering 1: - 1 API compatibility. CCMath covers the vast majority of mathematical operations, including trigonometric, exponential, - logarithmic, and more that are provided by the standard library. -- **Performance-Focused Design**: All implemented functions are optimized for both compile-time and runtime performance, - providing - comparable or faster execution than standard library implementations. -- **Cross-Platform Support**: Tested on major platforms (Windows, macOS, Linux) with GCC, Clang, MSVC, and more, CCMath - ensures compatibility across diverse environments. -- **No External Dependencies**: The library requires only a modern C++17-compliant compiler, with no additional - dependencies. -- **Header-Only Library**: Integration is straightforward—just include the headers in your project and start using - CCMath. +CCMath is a header-only library with a ``-style API. The same entry points constexpr-evaluate where the standard and compiler allow, dispatch to portable runtime implementations elsewhere, and can use SIMD on selected functions when the target supports it. + +This README describes what CCMath is working toward. Some claims here reflect goals still in progress. See [STATUS.md](docs/STATUS.md) for current implementation status by module and function. + +For approximation-driven function work, see the implementation guide +in [APPROXIMATING_FUNCTIONS.pdf](docs/approximating_functions/APPROXIMATING_FUNCTIONS.pdf). + +--- + +## Why CCMath? + +Most `` implementations prioritize different tradeoffs. Few combine standards-oriented semantics, constexpr evaluation, full runtime paths, SIMD, and portable in-tree code. + +| Library | Standards-Oriented | Constexpr | Runtime | SIMD | Portable Implementation | +|-------------------|--------------------|-----------|---------|------------------|-------------------------| +| `std::cmath` | Yes | Partial | Yes | Vendor dependent | No | +| Compiler builtins | Partial | Partial | Yes | No | No | +| GCEM | Partial | Yes | Limited | No | Yes | +| OpenLibm | Yes | No | Yes | Limited | Yes | +| SLEEF | Partial | No | Yes | Yes | Yes | +| CCMath | Yes | Yes | Yes | Yes | Yes | + +CCMath targets: + +- Standards conformance +- Compile-time evaluation +- Runtime execution +- SIMD acceleration +- Numerical correctness +- Cross-platform portability + +These goals overlap with the comparison columns plus numerical validation. Per-function progress is tracked in [STATUS.md](docs/STATUS.md). + +--- + +## Design Principles + +### Standards Conformance + +CCMath is designed to closely follow the behavior specified by the C and C++ standards for mathematical functions. + +This includes: + +- Function semantics +- Domain handling +- Range handling +- NaN propagation +- Infinity behavior +- Floating-point classification +- Rounding behavior +- Edge-case handling + +Implemented functions follow the rules the C and C++ standards set for ``. + +### Compile-Time Evaluation + +```cpp +constexpr auto value = ccm::sin(0.5); + +static_assert(value > 0.0); +``` + +There is no separate constexpr API. When constant evaluation is possible, the public symbol participates in it. + +### Runtime Execution + +```cpp +double result = ccm::exp(x); +``` + +When constant evaluation is not possible, the same symbols dispatch to runtime paths. + +### SIMD Acceleration + +Selected functions pick up SIMD on supported targets: + +- SSE2 +- SSE4 +- AVX2 +- ARM NEON + +### Numerical Correctness + +We aim for correct rounding under all four IEEE rounding modes. Coverage is function-by-function: some paths are validated today under all modes, while other functions still target round-to-nearest ties-to-even first. + +ULP harnesses, all-mode rounding probes, worst-case grids, and cross-compiler CI matrices live in-tree. The validation section below describes how they run in practice. + +--- + +## Function Coverage + +Work spans trigonometric, exponential, power, rounding, classification, and utility categories from the C and C++ mathematical library. Several modules are still in flight. [STATUS.md](docs/STATUS.md) lists what ships today and what remains open. + +--- + +## Validation and Verification + +Validation follows patterns from LLVM-libc, CORE-MATH, and glibc. CI covers: + +### Continuous Integration + +#### Platforms + +- Linux +- macOS +- Windows + +#### Compilers + +- GCC +- Clang +- Apple Clang +- MSVC + +#### Language Standards + +- C++17 +- C++20 + +#### Build Configurations + +- Debug +- Release + +### Simple Validation Suite + +The `simple` suite runs on every CI job: + +```bash +ctest -L simple +``` + +This suite exercises public `ccm::*` entry points and includes: + +- Smoke testing +- Standards edge cases +- ULP checks against the active platform libm +- Regression testing + +### Rigorous Validation Suite + +The `rigorous` suite runs in dedicated CI jobs: + +```bash +ctest -L rigorous +``` + +Rigorous jobs compare against MPFR and CORE-MATH oracles. + +#### MPFR Validation + +Within a rigorous build, MPFR-backed tests use the `mpfr` label: + +```bash +ctest -L mpfr +``` + +Includes: + +- Extended function corpora +- Exceptional-value matrices +- Adversarial search + +Linux and macOS use system MPFR/GMP. + +Windows uses MPFR provided through vcpkg. + +#### CORE-MATH Validation + +Within a rigorous build, CORE-MATH-backed tests use the `coremath` label: + +```bash +ctest -L coremath +``` + +Correctly-rounded reference comparisons under all four IEEE rounding modes on finite inputs. + +#### Proof Verification + +When available, CCMath uses: + +- Gappa +- Sollya + +to refresh and verify proof artifacts for supported implementations. + +### Fuzz Testing + +Fuzz smoke tests run in dedicated macOS and Linux CI jobs with full LLVM Clang (libFuzzer requires the fuzzer sanitizer runtime). Windows matrix jobs use MSVC and are outside libFuzzer coverage today. + +### Static Analysis and Security + +CodeQL, OpenSSF Scorecard, warning-as-error builds, and strict compiler warning gates run in CI. + +### Style and Consistency + +clang-format runs in CI on every platform matrix job and on pull requests that touch headers. clang-tidy is available locally via lint.sh and lint.bat. + +--- ## Usage -Include CCMath in your project by adding the `` header file. Here's a simple example: +### Monolithic Include ```cpp -#include // Monolithic header that includes all functions like +#include #include int main() { - constexpr double result = ccm::sqrt(25.0); // Compile-time evaluation - std::cout << "Square root of 25: " << result << std::endl; - return 0; + constexpr double result = ccm::sqrt(25.0); + + std::cout << result << '\n'; } ``` -or if you prefer to include only specific functions: +### Per-Function Include ```cpp -#include // Function-specific header for free-standing functions +#include #include int main() { - constexpr double result = ccm::log(12.0); // Compile-time evaluation - std::cout << "Log of 12: " << result << std::endl; - return 0; + constexpr double result = ccm::log(12.0); + + std::cout << result << '\n'; } ``` -### Adding CCMath via CMake +--- -CCMath includes a comprehensive CMake setup for easy integration. Use `FetchContent` as shown below: +## Installation + +### CMake ```cmake include(FetchContent) + FetchContent_Declare( - ccmath - GIT_REPOSITORY https://github.com/Rinzii/ccmath.git - GIT_TAG main # Or replace with a specific version (e.g., v0.2.0) + ccmath + GIT_REPOSITORY https://github.com/Rinzii/ccmath.git + GIT_TAG v0.3.0 ) + FetchContent_MakeAvailable(ccmath) -target_link_libraries(main PRIVATE ccmath::ccmath) +target_link_libraries( + main + PRIVATE + ccmath::ccmath +) +``` + +You may also vendor the headers directly. + +### Secondary build systems (Meson and Premake) + +CMake is the primary and authoritative build system. Meson and Premake support library consumption only: include paths, compile definitions, the generated `version.hpp`, and the library-required feature validation that a consumer needs. They do not define examples, smoke binaries, tests, benchmarks, fuzzing targets, or third-party dependency fetches. + +After configuring CMake, regenerate the secondary build files: + +```bash +cmake --preset ninja-clang-debug -DCCMATH_BUILD_TESTS=OFF -DCCMATH_BUILD_EXAMPLES=OFF +cmake --build out/build/ninja/ninja-clang-debug --target ccmath-generate-secondary-builds +``` + +Meson consumer usage after regeneration: + +```meson +ccmath_dep = dependency('ccmath', fallback : ['ccmath', 'ccmath_dep']) +executable('my-app', 'main.cpp', dependencies : ccmath_dep) +``` + +Premake consumer usage after regeneration: + +```lua +include("../ccmath/premake5.lua") + +project "my-app" + kind "ConsoleApp" + language "C++" + files { "main.cpp" } + ccmath.use() ``` -Alternatively, you can directly include the headers in your project. +The generated root `meson.build` exposes `ccmath_dep` and overrides the `ccmath` dependency name for Meson consumers. The generated root `premake5.lua` exposes `ccmath.use()`, `ccmath.include_dirs()`, and `ccmath.defines()` for Premake consumers. Library-facing options are declared once in `cmake/config/BuildManifest.cmake` and exported through `ccmath-build-manifest.json`. + +### Vendored integration testing + +[`integration/vendored-consumer`](integration/vendored-consumer) simulates an external project that vendors CCMath headers under `third_party/ccmath`. CI copies the public headers and generated `version.hpp`, then builds the consumer with CMake, Meson, and Premake. + +```bash +cmake --preset ninja-clang-debug -DCCMATH_BUILD_TESTS=OFF -DCCMATH_BUILD_EXAMPLES=OFF +bash tools/integration/prepare_vendored_tree.sh out/build/ninja/ninja-clang-debug + +cmake -S integration/vendored-consumer -B integration/vendored-consumer/build-cmake -G Ninja +cmake --build integration/vendored-consumer/build-cmake + +meson setup integration/vendored-consumer/build-meson integration/vendored-consumer +meson compile -C integration/vendored-consumer/build-meson + +cd integration/vendored-consumer && premake5 gmake && make config=debug +``` + +--- + +## Rigorous Testing Setup + +Typical local configuration: + +```bash +cmake -B build-rigorous -G Ninja \ + -DCCMATH_BUILD_RIGOROUS_TESTS=ON \ + -DCCMATH_ENABLE_MPFR_TESTS=ON \ + -DCCMATH_ENABLE_COREMATH_TESTS=ON \ + -DCCMATH_COREMATH_ROOT=/path/to/coremath-prefix +``` + +Build a CORE-MATH prefix using: + +```bash +tools/coremath/build_prefix.sh +``` + +On Windows, pass: + +```bash +-DVCPKG_MANIFEST_DIR=cmake/vcpkg +``` + +along with a vcpkg toolchain file to obtain MPFR. + +See the workflow definitions in [`.github/workflows`](.github/workflows) for the exact CI configurations. + +The library itself has no required dependencies. + +Optional packages used for testing, benchmarking, and fuzzing are documented in [thirdparty/README.md](thirdparty/README.md). + +--- ## Supported Compilers - GCC 11.1+ - Clang 9.0.0+ -- AppleClang 14.0.3+ +- Apple Clang 14.0.3+ - MSVC 19.26+ - Intel DPC++ 2022.0.0+ -- Nvidia HPC SDK 22.7+ - -> [!ATTENTION] -> Listed compilers may not be the actual supported versions as development is still ongoing. +- NVIDIA HPC SDK 22.7+ > [!NOTE] -> Currently working on finding manners to lower these requirements. - -## Contributing +> Minimum compiler versions are still being refined and should be considered guidance rather than a finalized support matrix. -CCMath is an open-source project, and contributions are always welcome! Whether you want to report bugs, suggest -features, or contribute code, check out our [contribution guidelines](CONTRIBUTING.md). +--- -## Join Our Discord! +## Projects Using CCMath -Have questions, ideas, or just want to chat? Join our Discord server—we’d love to hear from you! +The following projects publicly use CCMath: -[![Discord Banner](https://discord.com/api/guilds/1286067628456284244/widget.png?style=banner2)](https://discord.gg/p3mVxAbdmc) +- [Fornani](https://github.com/swagween/fornani) -## Implementation Status +If your project uses CCMath and is publicly available, open a PR to add it to this list. -For details on the current progress of the library, including completed features and planned additions, refer to -the [implementation status](docs/STATUS.md) document. +--- -## Projects Using CCMath +## Contributing -- [Fornani](https://github.com/swagween/fornani) - A 2D action-adventure game in development. +See [CONTRIBUTING.md](CONTRIBUTING.md), [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md), and [SECURITY.md](SECURITY.md). Bug reports and design questions go to GitHub Issues or Discord. -_(Are you using CCMath? Let us know!)_ +--- ## License -CCMath is licensed under the Apache-2.0 WITH LLVM-exception. See the LICENSE file for more details. +Apache License v2.0 with LLVM Exceptions. See [LICENSE](LICENSE) and [NOTICE](NOTICE). + +--- -## Special Thanks +## Acknowledgments -A special thanks to LLVM, GCC, and the many public LibM's for providing inspiration and insights during the development -of CCMath. +Algorithm and validation ideas draw on LLVM-libc, GCC libm, CORE-MATH, glibc math, and OpenLibm. diff --git a/SECURITY.md b/SECURITY.md index 5641c5e5..6b46c6e7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,39 +1,30 @@ -# Security Policy for CCMath - -## 1. Introduction -This security policy outlines the procedures and guidelines for reporting vulnerabilities and managing security issues related to the CCMath library. Our commitment is to provide only the essential support necessary to maintain the security of this project. - -## 2. Supported Versions -We only support the latest released version of the CCMath library, according to [Semantic Versioning (SemVer)](https://semver.org/). If you are using an older version, we strongly encourage you to upgrade to the latest version to ensure you receive any security updates. - -## 3. Reporting a Vulnerability -If you discover a security vulnerability in ccmath, please disclose it responsibly by following these steps: -1. **Contact**: Email the details to ianpike98(at)gmail(dot)com. Please include: - - A detailed description of the vulnerability. - - Steps to reproduce the issue. - - Any potential impact or threat associated with the vulnerability. -2. **Do Not**: Publicly disclose the vulnerability until we have had a chance to address it. -3. **Acknowledgment**: We will acknowledge your report within 7 days and provide an estimated timeline for addressing the vulnerability. - -## 4. Response Process -Upon receiving a vulnerability report, we will: -1. **Investigate**: Confirm and reproduce the issue. -2. **Assess**: Evaluate the severity and potential impact. -3. **Mitigate**: Develop and test a fix, if deemed necessary. -4. **Patch**: Release a patch only for the latest version. -5. **Notify**: Inform the reporter once the vulnerability is resolved. - -## 5. Public Disclosure -Once the vulnerability is resolved, we will: -- Issue a brief advisory outlining the vulnerability, its impact, and the resolution. -- Credit the individual who reported the vulnerability unless they wish to remain anonymous. - -## 6. Security Best Practices -We recommend that users of the CCMath library: -- Keep their CCMath version up to date by always using the latest release. -- Regularly review and apply security patches as they become available. -- Follow secure coding practices when integrating CCMath into their projects. - -## 7. Security Contact Information -If you have any questions or need further assistance, please contact us at ianpike98(at)gmail(dot)com. +# Security policy +## Supported versions + +Only the latest release tagged under [Semantic Versioning](https://semver.org/) gets +security fixes. Older tags are unsupported. + +## Reporting + +Email ianpike98(at)gmail(dot)com with: + +- What you found +- Steps to reproduce +- Impact you expect + +Do not open a public issue for an unfixed vulnerability. + +We aim to acknowledge within 7 days and send a rough fix timeline when the report +checks out. + +## Our response + +1. Reproduce and confirm +2. Decide severity +3. Patch on `dev`, ship in the next release if needed +4. Credit the reporter in the advisory unless they want anonymity + +## Contact + +Security questions: ianpike98(at)gmail(dot)com diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 11984720..cd67ee95 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -1,42 +1,23 @@ +cmake_minimum_required(VERSION 3.20 FATAL_ERROR) - -cmake_minimum_required(VERSION 3.18 FATAL_ERROR) - -project(ccmath-benchmark) +project(ccmath-benchmark LANGUAGES CXX) option(CCM_BENCH_BASIC "Enable basic benchmarks" OFF) option(CCM_BENCH_COMPARE "Enable comparison benchmarks" OFF) option(CCM_BENCH_POWER "Enable power benchmarks" OFF) option(CCM_BENCH_NEAREST "Enable nearest benchmarks" ON) - option(CCM_BENCH_ALL "Enable all benchmarks" OFF) -# Force cmake to use Release if debug is detected if (CMAKE_BUILD_TYPE STREQUAL "Debug") - set(CMAKE_BUILD_TYPE "Release" ON CACHE STRING "Build type" FORCE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE) endif () if (NOT TARGET ccmath) find_package(ccmath CONFIG REQUIRED) endif () -function(add_benchmark function_name source_files) - add_executable(ccm_benchmark_${function_name} helpers/randomizers.hpp ${source_files}) - target_link_libraries(ccm_benchmark_${function_name} PRIVATE ccmath::ccmath benchmark::benchmark) - target_compile_features(ccm_benchmark_${function_name} PRIVATE cxx_std_17) -endfunction() +set(CCMATH_BENCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) +include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/config/BenchmarkModuleRegistry.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/helpers/CcmBenchHelpers.cmake) - -if (CCM_BENCH_BASIC) - add_benchmark(abs benchmarks/basic/abs.bench.cpp benchmarks/basic/abs.bench.hpp) - add_benchmark(fdim benchmarks/basic/fdim.bench.cpp benchmarks/basic/fdim.bench.hpp) - add_benchmark(fma benchmarks/basic/fma.bench.cpp benchmarks/basic/fma.bench.hpp) -endif () - -if (CCM_BENCH_POWER) - add_benchmark(sqrt benchmarks/power/sqrt.bench.cpp benchmarks/power/sqrt.bench.hpp) -endif () - -if (CCM_BENCH_NEAREST) - #add_benchmark(trunc benchmarks/nearest/trunc.bench.cpp benchmarks/nearest/trunc.bench.hpp) -endif () +ccmath_apply_bench_registry() diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 00000000..1f9f74b3 --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,38 @@ +# Benchmark tooling + +BenchmarkModuleRegistry.cmake maps modules to function lists. CcmBenchHelpers.cmake builds ccm_benchmark_ executables via ccmath_apply_bench_registry. + +## Options + +| Option | Default | +| --- | --- | +| CCM_BENCH_BASIC | OFF | +| CCM_BENCH_POWER | OFF | +| CCM_BENCH_NEAREST | ON | +| CCM_BENCH_ALL | OFF | + +Aggregate target: ccm_benchmark_all. + +## Macros (benchmarks/shared/register.hpp) + +| Macro | Registers | +| --- | --- | +| CCMATH_BENCH_UNARY_COMPARE | std vs ccm on scalar and random vectors, CT path | +| CCMATH_BENCH_*_COMPARE_PATHS | Same with _rt suites when the ccm body forces runtime | +| CCMATH_BENCH_*_PROFILE | Fixed literal, named for asmlab profiles | +| CCMATH_BENCH_*_PROFILE_PATHS | Profile with CT and RT | + +Per-target path opt-out: CCM_BM_CONFIG_NO_CT or CCM_BM_CONFIG_NO_RT through ccmath_add_bench_target COMPILE_DEFINITIONS. + +## New function + +Add benchmarks/src/math//foo.bench.cpp, append foo to CCMATH_BENCH_MODULE__FUNCTIONS, enable the module option at configure. + +## Run + +```bash +cmake --preset -DCCM_BENCH_POWER=ON +cmake --build out/ --target ccm_benchmark_sqrt +``` + +powf_impl and other impl-kernel benches are asmlab targets under tools/asmlab/bench/, added when CCM_BENCH_POWER is on. diff --git a/benchmarks/benchmarks/basic/abs.bench.cpp b/benchmarks/benchmarks/basic/abs.bench.cpp deleted file mode 100644 index 3781303d..00000000 --- a/benchmarks/benchmarks/basic/abs.bench.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#include "abs.bench.hpp" - -// NOLINTBEGIN - - -BENCHMARK(BM_basic_abs_std)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_abs_ccmath)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_abs_rand_int_std)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_abs_rand_int_ccmath)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_abs_rand_double_std)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_abs_rand_double_ccmath)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK_MAIN(); - -// NOLINTEND \ No newline at end of file diff --git a/benchmarks/benchmarks/basic/abs.bench.hpp b/benchmarks/benchmarks/basic/abs.bench.hpp deleted file mode 100644 index 040d63cd..00000000 --- a/benchmarks/benchmarks/basic/abs.bench.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* -* Copyright (c) 2024-Present Ian Pike -* Copyright (c) 2024-Present ccmath contributors -* -* This library is provided under the MIT License. -* See LICENSE for more information. -*/ - -#include "../../helpers/randomizers.hpp" -#include -#include -#include - -namespace bm = benchmark; - -// NOLINTBEGIN - - -static void BM_basic_abs_std(benchmark::State& state) { - for ([[maybe_unused]] auto _ : state) { - benchmark::DoNotOptimize(std::abs(state.range(0))); - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_abs_ccmath(benchmark::State& state) { - for ([[maybe_unused]] auto _ : state) { - benchmark::DoNotOptimize(ccm::abs(state.range(0))); - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_abs_rand_int_std(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomIntegers = ran.generateRandomIntegers(state.range(0)); - while (state.KeepRunning()) { - for (auto x : randomIntegers) { - benchmark::DoNotOptimize(std::abs(x)); - } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_abs_rand_int_ccmath(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomIntegers = ran.generateRandomIntegers(state.range(0)); - while (state.KeepRunning()) { - for (auto x : randomIntegers) { - benchmark::DoNotOptimize(ccm::abs(x)); - } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_abs_rand_double_std(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomDoubles = ran.generateRandomDoubles(state.range(0)); - while (state.KeepRunning()) { - for (auto x : randomDoubles) { - benchmark::DoNotOptimize(std::abs(x)); - } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_abs_rand_double_ccmath(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomDoubles = ran.generateRandomDoubles(state.range(0)); - while (state.KeepRunning()) { - for (auto x : randomDoubles) { - benchmark::DoNotOptimize(ccm::abs(x)); - } - } - state.SetComplexityN(state.range(0)); -} - - -// NOLINTEND \ No newline at end of file diff --git a/benchmarks/benchmarks/basic/fdim.bench.cpp b/benchmarks/benchmarks/basic/fdim.bench.cpp deleted file mode 100644 index b25b95ff..00000000 --- a/benchmarks/benchmarks/basic/fdim.bench.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* -* Copyright (c) 2024-Present Ian Pike -* Copyright (c) 2024-Present ccmath contributors -* -* This library is provided under the MIT License. -* See LICENSE for more information. -*/ - -#include "fdim.bench.hpp" - -// NOLINTBEGIN - -BENCHMARK(BM_basic_fdim_std)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_fdim_ccmath)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_fdim_rand_int_std)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_fdim_rand_int_ccmath)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_fdim_rand_double_std)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_fdim_rand_double_ccmath)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK_MAIN(); - -// NOLINTEND \ No newline at end of file diff --git a/benchmarks/benchmarks/basic/fdim.bench.hpp b/benchmarks/benchmarks/basic/fdim.bench.hpp deleted file mode 100644 index ffcb3634..00000000 --- a/benchmarks/benchmarks/basic/fdim.bench.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* -* Copyright (c) 2024-Present Ian Pike -* Copyright (c) 2024-Present ccmath contributors -* -* This library is provided under the MIT License. -* See LICENSE for more information. - */ - -#include "../../helpers/randomizers.hpp" -#include -#include -#include - -namespace bm = benchmark; - -// NOLINTBEGIN - -static void BM_basic_fdim_std(benchmark::State& state) { - for ([[maybe_unused]] auto _ : state) { - benchmark::DoNotOptimize(std::fdim(state.range(0), state.range(1))); - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_fdim_ccmath(benchmark::State& state) { - for ([[maybe_unused]] auto _ : state) { - benchmark::DoNotOptimize(ccm::fdim(state.range(0), state.range(1))); - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_fdim_rand_int_std(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomIntegers = ran.generateRandomIntegers(state.range(0)); - auto randomIntegers2 = ran.generateRandomIntegers(state.range(0)); - while (state.KeepRunning()) { - for (size_t i = 0; i < randomIntegers.size(); ++i) { - benchmark::DoNotOptimize(std::fdim(randomIntegers[i], randomIntegers2[i])); - } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_fdim_rand_int_ccmath(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomIntegers = ran.generateRandomIntegers(state.range(0)); - auto randomIntegers2 = ran.generateRandomIntegers(state.range(0)); - while (state.KeepRunning()) { - for (size_t i = 0; i < randomIntegers.size(); ++i) { - benchmark::DoNotOptimize(ccm::fdim(randomIntegers[i], randomIntegers2[i])); - } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_fdim_rand_double_std(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomDoubles = ran.generateRandomDoubles(state.range(0)); - auto randomDoubles2 = ran.generateRandomDoubles(state.range(0)); - while (state.KeepRunning()) { - for (size_t i = 0; i < randomDoubles.size(); ++i) { - benchmark::DoNotOptimize(std::fdim(randomDoubles[i], randomDoubles2[i])); - } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_fdim_rand_double_ccmath(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomDoubles = ran.generateRandomDoubles(state.range(0)); - auto randomDoubles2 = ran.generateRandomDoubles(state.range(0)); - while (state.KeepRunning()) { - for (size_t i = 0; i < randomDoubles.size(); ++i) { - benchmark::DoNotOptimize(ccm::fdim(randomDoubles[i], randomDoubles2[i])); - } - } - state.SetComplexityN(state.range(0)); -} - -// NOLINTEND \ No newline at end of file diff --git a/benchmarks/benchmarks/basic/fma.bench.cpp b/benchmarks/benchmarks/basic/fma.bench.cpp deleted file mode 100644 index 30711ede..00000000 --- a/benchmarks/benchmarks/basic/fma.bench.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* -* Copyright (c) 2024-Present Ian Pike -* Copyright (c) 2024-Present ccmath contributors -* -* This library is provided under the MIT License. -* See LICENSE for more information. -*/ - -#include "fma.bench.hpp" - -// NOLINTBEGIN - -BENCHMARK(BM_basic_fma_std)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_fma_ccmath)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_fma_rand_int_std)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_fma_rand_int_ccmath)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_fma_rand_double_std)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK(BM_basic_fma_rand_double_ccmath)->RangeMultiplier(2)->Range(8, 8<<10)->Complexity(); - -BENCHMARK_MAIN(); - -// NOLINTEND \ No newline at end of file diff --git a/benchmarks/benchmarks/basic/fma.bench.hpp b/benchmarks/benchmarks/basic/fma.bench.hpp deleted file mode 100644 index 4a105efb..00000000 --- a/benchmarks/benchmarks/basic/fma.bench.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* -* Copyright (c) 2024-Present Ian Pike -* Copyright (c) 2024-Present ccmath contributors -* -* This library is provided under the MIT License. -* See LICENSE for more information. - */ - -#include "../../helpers/randomizers.hpp" -#include -#include -#include - -namespace bm = benchmark; - -// NOLINTBEGIN - -static void BM_basic_fma_std(benchmark::State& state) { - for ([[maybe_unused]] auto _ : state) { - benchmark::DoNotOptimize(std::fma(state.range(0), state.range(1), state.range(2))); - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_fma_ccmath(benchmark::State& state) { - for ([[maybe_unused]] auto _ : state) { - benchmark::DoNotOptimize(ccm::fma(state.range(0), state.range(1), state.range(2))); - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_fma_rand_int_std(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomIntegers = ran.generateRandomIntegers(state.range(0)); - auto randomIntegers2 = ran.generateRandomIntegers(state.range(0)); - auto randomIntegers3 = ran.generateRandomIntegers(state.range(0)); - while (state.KeepRunning()) { - for (size_t i = 0; i < randomIntegers.size(); ++i) { - benchmark::DoNotOptimize(std::fma(randomIntegers[i], randomIntegers2[i], randomIntegers3[i])); - } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_fma_rand_int_ccmath(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomIntegers = ran.generateRandomIntegers(state.range(0)); - auto randomIntegers2 = ran.generateRandomIntegers(state.range(0)); - auto randomIntegers3 = ran.generateRandomIntegers(state.range(0)); - while (state.KeepRunning()) { - for (size_t i = 0; i < randomIntegers.size(); ++i) { - benchmark::DoNotOptimize(ccm::fma(randomIntegers[i], randomIntegers2[i], randomIntegers3[i])); - } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_fma_rand_double_std(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomDoubles = ran.generateRandomDoubles(state.range(0)); - auto randomDoubles2 = ran.generateRandomDoubles(state.range(0)); - auto randomDoubles3 = ran.generateRandomDoubles(state.range(0)); - while (state.KeepRunning()) { - for (size_t i = 0; i < randomDoubles.size(); ++i) { - benchmark::DoNotOptimize(std::fma(randomDoubles[i], randomDoubles2[i], randomDoubles3[i])); - } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_basic_fma_rand_double_ccmath(benchmark::State& state) { - ccm::bench::Randomizer ran; - auto randomDoubles = ran.generateRandomDoubles(state.range(0)); - auto randomDoubles2 = ran.generateRandomDoubles(state.range(0)); - auto randomDoubles3 = ran.generateRandomDoubles(state.range(0)); - while (state.KeepRunning()) { - for (size_t i = 0; i < randomDoubles.size(); ++i) { - benchmark::DoNotOptimize(ccm::fma(randomDoubles[i], randomDoubles2[i], randomDoubles3[i])); - } - } - state.SetComplexityN(state.range(0)); -} - -// NOLINTEND \ No newline at end of file diff --git a/benchmarks/benchmarks/nearest/trunc.bench.hpp b/benchmarks/benchmarks/nearest/trunc.bench.hpp deleted file mode 100644 index e69de29b..00000000 diff --git a/benchmarks/benchmarks/power/sqrt.bench.cpp b/benchmarks/benchmarks/power/sqrt.bench.cpp deleted file mode 100644 index 0e8c0f88..00000000 --- a/benchmarks/benchmarks/power/sqrt.bench.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#include "sqrt.bench.hpp" - -// NOLINTBEGIN - -// Regular -#ifndef CCM_BM_CONFIG_NO_CT - -BENCHMARK(BM_power_sqrt_std)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); - -BENCHMARK(BM_power_sqrt_ccm)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); - -BENCHMARK(BM_power_sqrt_rand_int_std)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); - -BENCHMARK(BM_power_sqrt_rand_int_ccm)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); - -BENCHMARK(BM_power_sqrt_rand_double_std)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); - -BENCHMARK(BM_power_sqrt_rand_double_ccm)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); -#endif - -#ifndef CCM_BM_CONFIG_NO_CT - // Runtime - #if !(defined(__GNUC__) && (__GNUC__ > 6 || (__GNUC__ == 6 && __GNUC_MINOR__ >= 1)) && !defined(__clang__)) // GCC 6.1+ has constexpr sqrt builtins. - -BENCHMARK(BM_power_sqrt_std_rt)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); - -BENCHMARK(BM_power_sqrt_ccm_rt)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); - -BENCHMARK(BM_power_sqrt_rand_int_std_rt)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); - -BENCHMARK(BM_power_sqrt_rand_int_ccm_rt)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); - -BENCHMARK(BM_power_sqrt_rand_double_std_rt)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); - -BENCHMARK(BM_power_sqrt_rand_double_ccm_rt)->RangeMultiplier(2)->Range(8, 8 << 10)->Complexity(); - - #endif -#endif - -BENCHMARK_MAIN(); - -// NOLINTEND \ No newline at end of file diff --git a/benchmarks/benchmarks/power/sqrt.bench.hpp b/benchmarks/benchmarks/power/sqrt.bench.hpp deleted file mode 100644 index 8285da6e..00000000 --- a/benchmarks/benchmarks/power/sqrt.bench.hpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#include "../../helpers/randomizers.hpp" - -#include -#include -#include - -namespace bm = benchmark; - -// NOLINTBEGIN -// std::sqrt - - -#ifndef CCM_BM_CONFIG_NO_CT -static void BM_power_sqrt_std(benchmark::State & state) -{ - for ([[maybe_unused]] auto _ : state) { benchmark::DoNotOptimize(std::sqrt(static_cast(state.range(0)))); } - state.SetComplexityN(state.range(0)); -} - -static void BM_power_sqrt_ccm(benchmark::State & state) -{ - for ([[maybe_unused]] auto _ : state) { benchmark::DoNotOptimize(ccm::sqrt(static_cast(state.range(0)))); } - state.SetComplexityN(state.range(0)); -} - -static void BM_power_sqrt_rand_int_std(benchmark::State & state) -{ - ccm::bench::Randomizer ran; - auto randomIntegers = ran.generateRandomIntegers(state.range(0)); - while (state.KeepRunning()) - { - for (auto x : randomIntegers) { benchmark::DoNotOptimize(std::sqrt(static_cast(x))); } - } - state.SetComplexityN(state.range(0)); -} -// -static void BM_power_sqrt_rand_int_ccm(benchmark::State & state) -{ - ccm::bench::Randomizer ran; - auto randomIntegers = ran.generateRandomIntegers(state.range(0)); - while (state.KeepRunning()) - { - for (auto x : randomIntegers) { benchmark::DoNotOptimize(ccm::sqrt(static_cast(x))); } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_power_sqrt_rand_double_std(benchmark::State & state) -{ - ccm::bench::Randomizer ran; - auto randomDoubles = ran.generateRandomDoubles(state.range(0)); - while (state.KeepRunning()) - { - for (auto x : randomDoubles) { benchmark::DoNotOptimize(std::sqrt(x)); } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_power_sqrt_rand_double_ccm(benchmark::State & state) -{ - ccm::bench::Randomizer ran; - auto randomDoubles = ran.generateRandomDoubles(state.range(0)); - while (state.KeepRunning()) - { - for (auto x : randomDoubles) { benchmark::DoNotOptimize(ccm::sqrt(x)); } - } - state.SetComplexityN(state.range(0)); -} - -#endif - -// Runtime evaluation - -#ifndef CCM_BM_CONFIG_NO_RT - #if !(defined(__GNUC__) && (__GNUC__ > 6 || (__GNUC__ == 6 && __GNUC_MINOR__ >= 1)) && !defined(__clang__)) // GCC 6.1+ has constexpr sqrt builtins. - -inline double sqrt_rt_bm(double v) -{ - return ccm::sqrt(v); -} - -static void BM_power_sqrt_std_rt(benchmark::State & state) -{ - for ([[maybe_unused]] auto _ : state) { benchmark::DoNotOptimize(std::sqrt(static_cast(state.range(0)))); } - state.SetComplexityN(state.range(0)); -} - -static void BM_power_sqrt_ccm_rt(benchmark::State & state) -{ - for ([[maybe_unused]] auto _ : state) { benchmark::DoNotOptimize(sqrt_rt_bm(static_cast(state.range(0)))); } - state.SetComplexityN(state.range(0)); -} - -static void BM_power_sqrt_rand_int_std_rt(benchmark::State & state) -{ - ccm::bench::Randomizer ran; - auto randomIntegers = ran.generateRandomIntegers(state.range(0)); - while (state.KeepRunning()) - { - for (auto x : randomIntegers) { benchmark::DoNotOptimize(std::sqrt(static_cast(x))); } - } - state.SetComplexityN(state.range(0)); -} -// -static void BM_power_sqrt_rand_int_ccm_rt(benchmark::State & state) -{ - ccm::bench::Randomizer ran; - auto randomIntegers = ran.generateRandomIntegers(state.range(0)); - while (state.KeepRunning()) - { - for (auto x : randomIntegers) { benchmark::DoNotOptimize(sqrt_rt_bm(static_cast(x))); } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_power_sqrt_rand_double_std_rt(benchmark::State & state) -{ - ccm::bench::Randomizer ran; - auto randomDoubles = ran.generateRandomDoubles(state.range(0)); - while (state.KeepRunning()) - { - for (auto x : randomDoubles) { benchmark::DoNotOptimize(std::sqrt(x)); } - } - state.SetComplexityN(state.range(0)); -} - -static void BM_power_sqrt_rand_double_ccm_rt(benchmark::State & state) -{ - ccm::bench::Randomizer ran; - auto randomDoubles = ran.generateRandomDoubles(state.range(0)); - while (state.KeepRunning()) - { - for (auto x : randomDoubles) { benchmark::DoNotOptimize(sqrt_rt_bm(x)); } - } - state.SetComplexityN(state.range(0)); -} - - #endif -#endif - -// NOLINTEND \ No newline at end of file diff --git a/benchmarks/ccmath_benchmark_main.cpp b/benchmarks/ccmath_benchmark_main.cpp deleted file mode 100644 index 57137ce8..00000000 --- a/benchmarks/ccmath_benchmark_main.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#include -#include -#include - -#include -#include "ccmath/ccmath.hpp" -#include "helpers/randomizers.hpp" - -// NOLINTBEGIN - -namespace bm = benchmark; - -static ccm::bench::Randomizer ran{}; - -static void BM_basic_abs_ccm(benchmark::State& state) { - auto randomIntegers = ran.generateRandomIntegers(static_cast(state.range(0))); - while (state.KeepRunning()) { - for (auto x : randomIntegers) { - benchmark::DoNotOptimize(std::abs(x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_basic_abs_ccm)->Range(8, 8<<10)->Complexity(); - -static void BM_basic_abs_std(benchmark::State& state) { - auto randomIntegers = ran.generateRandomIntegers(static_cast(state.range(0))); - while (state.KeepRunning()) { - for (auto x : randomIntegers) { - benchmark::DoNotOptimize(ccm::abs(x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_basic_abs_std)->Range(8, 8<<10)->Complexity(); - - - - -/* - -static void BM_std_fma(bm::State& state) { - for ([[maybe_unused]] auto _ : state) { - bm::DoNotOptimize(std::fma(state.range(0), state.range(1), state.range(2))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_std_fma)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); - -static void BM_ccm_fma(bm::State& state) { - for ([[maybe_unused]] auto _ : state) { - bm::DoNotOptimize(ccm::fma(state.range(0), state.range(1), state.range(2))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_fma)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); - -*/ - -/* -// Benchmarking std::abs with the same set of random integers -static void BM_std_abs_rand_int(benchmark::State& state) { - auto randomIntegers = generateRandomIntegers(static_cast(state.range(0)), DefaultSeed); - while (state.KeepRunning()) { - for (auto x : randomIntegers) { - benchmark::DoNotOptimize(std::abs(x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_std_abs_rand_int)->Range(8, 8<<10)->Complexity(); - -// Benchmarking ccm::abs with the same set of random integers -static void BM_ccm_abs_rand_int(benchmark::State& state) { - auto randomIntegers = generateRandomIntegers(static_cast(state.range(0)), DefaultSeed); - while (state.KeepRunning()) { - for (auto x : randomIntegers) { - benchmark::DoNotOptimize(ccm::abs(x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_abs_rand_int)->Range(8, 8<<10)->Complexity(); -*/ -/* - -// Benchmarking std::abs with the same set of random integers -static void BM_std_abs_rand_double(benchmark::State& state) { - auto randomIntegers = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); - while (state.KeepRunning()) { - for (auto x : randomIntegers) { - benchmark::DoNotOptimize(std::abs(x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_std_abs_rand_double)->Range(8, 8<<10)->Complexity(); - -// Benchmarking ccm::abs with the same set of random integers -static void BM_ccm_abs_rand_double(benchmark::State& state) { - auto randomIntegers = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); - while (state.KeepRunning()) { - for (auto x : randomIntegers) { - benchmark::DoNotOptimize(ccm::abs(x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_abs_rand_double)->Range(8, 8<<10)->Complexity(); - - -static void BM_std_abs(benchmark::State& state) { - for (auto _ : state) { - benchmark::DoNotOptimize(std::abs(state.range(0))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_std_abs)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); - -static void BM_ccm_abs(benchmark::State& state) { - for (auto _ : state) { - benchmark::DoNotOptimize(ccm::abs(state.range(0))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_abs)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); - - -static void BM_std_log_rand_double(bm::State& state) { - auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); - while (state.KeepRunning()) { - for (auto x : randomDoubles) { - bm::DoNotOptimize(std::log(x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_std_log_rand_double)->Range(8, 8<<10)->Complexity(); - -static void BM_ccm_log_rand_double(bm::State& state) { - auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); - while (state.KeepRunning()) { - for (auto x : randomDoubles) { - bm::DoNotOptimize(ccm::log(x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_log_rand_double)->Range(8, 8<<10)->Complexity(); - -static void BM_ccm_log(bm::State& state) { - for (auto _ : state) { - bm::DoNotOptimize(ccm::log(state.range(0))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_log)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); - -static void BM_std_log(bm::State& state) { - for (auto _ : state) { - bm::DoNotOptimize(std::log(state.range(0))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_std_log)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); - -static void BM_std_log2_rand_double(bm::State& state) { - auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); - while (state.KeepRunning()) { - for (auto x : randomDoubles) { - bm::DoNotOptimize(std::log2(x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_std_log2_rand_double)->Range(8, 8<<10)->Complexity(); - -static void BM_ccm_log2_rand_double(bm::State& state) { - auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); - while (state.KeepRunning()) { - for (auto x : randomDoubles) { - bm::DoNotOptimize(ccm::log2(x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_log2_rand_double)->Range(8, 8<<10)->Complexity(); - -static void BM_ccm_log2(bm::State& state) { - for (auto _ : state) { - bm::DoNotOptimize(ccm::log2(state.range(0))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_log2)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); - -static void BM_std_log2(bm::State& state) { - for (auto _ : state) { - bm::DoNotOptimize(std::log2(state.range(0))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_std_log2)->Arg(16)->Arg(256)->Arg(4096)->Arg(65536)->Complexity(); - -static void BM_ccm_lerp(bm::State& state) { - for (auto _ : state) { - bm::DoNotOptimize(ccm::lerp(state.range(0), state.range(1), state.range(2))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_lerp)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); - -static void BM_ccm_lerp2(bm::State& state) { - for (auto _ : state) { - bm::DoNotOptimize(ccm::lerp2(state.range(0), state.range(1), state.range(2))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_lerp2)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); - - -static void BM_std_lerp(bm::State& state) { - for (auto _ : state) { - bm::DoNotOptimize(ccm::lerp(state.range(0), state.range(1), state.range(2))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_std_lerp)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); - -*/ - -// fma -static void BM_std_fma(bm::State& state) { - for ([[maybe_unused]] auto _ : state) { - bm::DoNotOptimize(std::fma(state.range(0), state.range(1), state.range(2))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_std_fma)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); - -static void BM_ccm_fma(bm::State& state) { - for ([[maybe_unused]] auto _ : state) { - bm::DoNotOptimize(ccm::fma(state.range(0), state.range(1), state.range(2))); - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_fma)->Args({16, 16, 16})->Args({256, 256, 256})->Args({4096, 4096, 4096})->Args({65536, 65536, 65536})->Complexity(); - -static void BM_std_fma_rand_double(bm::State& state) { - auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); - while (state.KeepRunning()) { - for (auto x : randomDoubles) { - bm::DoNotOptimize(std::fma(x, x, x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_std_fma_rand_double)->Range(8, 8<<10)->Complexity(); - -static void BM_ccm_fma_rand_double(bm::State& state) { - auto randomDoubles = generateRandomDoubles(static_cast(state.range(0)), DefaultSeed); - while (state.KeepRunning()) { - for (auto x : randomDoubles) { - bm::DoNotOptimize(ccm::fma(x, x, x)); - } - } - state.SetComplexityN(state.range(0)); -} -BENCHMARK(BM_ccm_fma_rand_double)->Range(8, 8<<10)->Complexity(); - - - -BENCHMARK_MAIN(); - - -// NOLINTEND diff --git a/benchmarks/helpers/randomizers.hpp b/benchmarks/helpers/randomizers.hpp deleted file mode 100644 index d7a700de..00000000 --- a/benchmarks/helpers/randomizers.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2024-Present Ian Pike - * Copyright (c) 2024-Present ccmath contributors - * - * This library is provided under the MIT License. - * See LICENSE for more information. - */ - -#pragma once - -#include -#include -#include - -namespace ccm::bench -{ - struct Randomizer - { - public: - explicit Randomizer(std::uint_fast32_t seed = 937162211) : m_gen{seed} {} - - std::vector generateRandomIntegers(std::int64_t count, int min = std::numeric_limits::min(), int max = std::numeric_limits::max()) - { - assert(count > 0); - std::vector randomIntegers; - randomIntegers.reserve(static_cast(count)); - std::uniform_int_distribution dist(min, max); - for (std::int64_t i = 0; i < count; ++i) { randomIntegers.push_back(dist(m_gen)); } - return randomIntegers; - } - - std::vector generateRandomDoubles(std::int64_t count, double min = 0.0, - double max = 1.0) - { - assert(count > 0); - std::vector randomDouble; - randomDouble.reserve(static_cast(count)); - std::uniform_real_distribution dist(min, max); - for (std::int64_t i = 0; i < count; ++i) { randomDouble.push_back(dist(m_gen)); } - return randomDouble; - } - - private: - std::mt19937 m_gen; - }; -} // namespace ccm::bench \ No newline at end of file diff --git a/benchmarks/shared/harness.hpp b/benchmarks/shared/harness.hpp new file mode 100644 index 00000000..0756a669 --- /dev/null +++ b/benchmarks/shared/harness.hpp @@ -0,0 +1,104 @@ +#pragma once + +#include "inputs.hpp" + +#include + +#include +#include +#include +#include + +namespace ccm::bench +{ + inline constexpr std::int64_t kRangeMin = 8; + inline constexpr std::int64_t kRangeMax = 8 << 10; + + template + T load_arg(T value) + { + volatile T storage = value; + return storage; + } + + template + void unary_fixed(benchmark::State& state, T value, Fn&& fn) + { + T const x = load_arg(value); + for (auto _ : state) { benchmark::DoNotOptimize(fn(x)); } + } + + template + void binary_fixed(benchmark::State& state, T x_value, T y_value, Fn&& fn) + { + T const x = load_arg(x_value); + T const y = load_arg(y_value); + for (auto _ : state) { benchmark::DoNotOptimize(fn(x, y)); } + } + + template + void ternary_fixed(benchmark::State& state, T x_value, T y_value, T z_value, Fn&& fn) + { + T const x = load_arg(x_value); + T const y = load_arg(y_value); + T const z = load_arg(z_value); + for (auto _ : state) { benchmark::DoNotOptimize(fn(x, y, z)); } + } + + template + void unary_scalar(benchmark::State& state, Fn&& fn) + { + for (auto _ : state) { benchmark::DoNotOptimize(fn(static_cast(state.range(0)))); } + state.SetComplexityN(state.range(0)); + } + + template + void unary_vector(benchmark::State& state, Fn&& fn, Vec const& inputs) + { + for (auto _ : state) + { + for (auto const& x : inputs) { benchmark::DoNotOptimize(fn(x)); } + } + state.SetComplexityN(state.range(0)); + } + + template + void binary_scalar(benchmark::State& state, Fn&& fn) + { + T const x = static_cast(state.range(0)); + T const y = static_cast(state.range(0)); + for (auto _ : state) { benchmark::DoNotOptimize(fn(x, y)); } + state.SetComplexityN(state.range(0)); + } + + template + void ternary_scalar(benchmark::State& state, Fn&& fn) + { + T const x = static_cast(state.range(0)); + T const y = static_cast(state.range(0)); + T const z = static_cast(state.range(0)); + for (auto _ : state) { benchmark::DoNotOptimize(fn(x, y, z)); } + state.SetComplexityN(state.range(0)); + } + + template + void ternary_vector(benchmark::State& state, Fn&& fn, VecX const& xs, VecY const& ys, VecZ const& zs) + { + for (auto _ : state) + { + for (std::size_t i = 0; i < xs.size(); ++i) { benchmark::DoNotOptimize(fn(xs[i], ys[i], zs[i])); } + } + state.SetComplexityN(state.range(0)); + } + + template + void binary_vector(benchmark::State& state, Fn&& fn, VecX const& xs, VecY const& ys) + { + for (auto _ : state) + { + for (std::size_t i = 0; i < xs.size(); ++i) { benchmark::DoNotOptimize(fn(xs[i], ys[i])); } + } + state.SetComplexityN(state.range(0)); + } + +} // namespace ccm::bench diff --git a/benchmarks/shared/inputs.hpp b/benchmarks/shared/inputs.hpp new file mode 100644 index 00000000..55fdbbae --- /dev/null +++ b/benchmarks/shared/inputs.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace ccm::bench +{ + struct Randomizer + { + explicit Randomizer(std::uint_fast32_t seed = 937162211) : m_gen{ seed } {} + + std::vector integers(std::int64_t count, int min = std::numeric_limits::min(), int max = std::numeric_limits::max()) + { + assert(count > 0); + std::vector out; + out.reserve(static_cast(count)); + std::uniform_int_distribution dist(min, max); + for (std::int64_t i = 0; i < count; ++i) { out.push_back(dist(m_gen)); } + return out; + } + + std::vector doubles(std::int64_t count, double min = 0.0, double max = 1.0) + { + assert(count > 0); + std::vector out; + out.reserve(static_cast(count)); + std::uniform_real_distribution dist(min, max); + for (std::int64_t i = 0; i < count; ++i) { out.push_back(dist(m_gen)); } + return out; + } + + private: + std::mt19937 m_gen; + }; + + inline Randomizer& thread_rng() + { + static thread_local Randomizer rng; + return rng; + } + + inline std::vector random_integers(std::int64_t count) + { return thread_rng().integers(count); } + + inline std::vector random_doubles(std::int64_t count) + { return thread_rng().doubles(count); } + +} // namespace ccm::bench diff --git a/benchmarks/shared/paths.hpp b/benchmarks/shared/paths.hpp new file mode 100644 index 00000000..d213659d --- /dev/null +++ b/benchmarks/shared/paths.hpp @@ -0,0 +1,35 @@ +#pragma once + +// Compile-time path guards for benchmark suites. +// Per-target: -DCCM_BM_CONFIG_NO_CT or -DCCM_BM_CONFIG_NO_RT via CMake. + +#if !defined(CCM_BM_CONFIG_NO_CT) +#define CCMATH_BENCH_HAS_CT 1 +#else +#define CCMATH_BENCH_HAS_CT 0 +#endif + +#if !defined(CCM_BM_CONFIG_NO_RT) +#define CCMATH_BENCH_HAS_RT 1 +#else +#define CCMATH_BENCH_HAS_RT 0 +#endif + +// GCC libstdc++ constexpr std::sqrt makes RT-vs-std comparison meaningless on GCC 6.1+. +#if CCMATH_BENCH_HAS_RT && (defined(__GNUC__) && (__GNUC__ > 6 || (__GNUC__ == 6 && __GNUC_MINOR__ >= 1)) && !defined(__clang__)) +#define CCMATH_BENCH_HAS_RT_SUITE 0 +#else +#define CCMATH_BENCH_HAS_RT_SUITE CCMATH_BENCH_HAS_RT +#endif + +#if CCMATH_BENCH_HAS_CT +#define CCMATH_BENCH_IF_CT(...) __VA_ARGS__ +#else +#define CCMATH_BENCH_IF_CT(...) +#endif + +#if CCMATH_BENCH_HAS_RT_SUITE +#define CCMATH_BENCH_IF_RT(...) __VA_ARGS__ +#else +#define CCMATH_BENCH_IF_RT(...) +#endif diff --git a/benchmarks/shared/register.hpp b/benchmarks/shared/register.hpp new file mode 100644 index 00000000..279a6127 --- /dev/null +++ b/benchmarks/shared/register.hpp @@ -0,0 +1,209 @@ +#pragma once + +#include "harness.hpp" +#include "paths.hpp" + +#include + +#include + +#define CCMATH_BENCH_APPLY_RANGE ->RangeMultiplier(2)->Range(ccm::bench::kRangeMin, ccm::bench::kRangeMax)->Complexity() + +#define CCMATH_BENCH_DECLARE_UNARY_SUITE(Module, Name, Suffix, StdBody, CcmBody) \ + static void BM_##Module##_##Name##_std##Suffix##_scalar(benchmark::State& state) \ + { ccm::bench::unary_scalar(state, [&](std::int64_t x) { StdBody; }); } \ + static void BM_##Module##_##Name##_ccm##Suffix##_scalar(benchmark::State& state) \ + { ccm::bench::unary_scalar(state, [&](std::int64_t x) { CcmBody; }); } \ + static void BM_##Module##_##Name##_std##Suffix##_rand_int(benchmark::State& state) \ + { \ + auto inputs = ccm::bench::random_integers(state.range(0)); \ + ccm::bench::unary_vector(state, [&](int x) { StdBody; }, inputs); \ + } \ + static void BM_##Module##_##Name##_ccm##Suffix##_rand_int(benchmark::State& state) \ + { \ + auto inputs = ccm::bench::random_integers(state.range(0)); \ + ccm::bench::unary_vector(state, [&](int x) { CcmBody; }, inputs); \ + } \ + static void BM_##Module##_##Name##_std##Suffix##_rand_double(benchmark::State& state) \ + { \ + auto inputs = ccm::bench::random_doubles(state.range(0)); \ + ccm::bench::unary_vector(state, [&](double x) { StdBody; }, inputs); \ + } \ + static void BM_##Module##_##Name##_ccm##Suffix##_rand_double(benchmark::State& state) \ + { \ + auto inputs = ccm::bench::random_doubles(state.range(0)); \ + ccm::bench::unary_vector(state, [&](double x) { CcmBody; }, inputs); \ + } + +#define CCMATH_BENCH_REGISTER_UNARY_SUITE(Module, Name, Suffix) \ + BENCHMARK(BM_##Module##_##Name##_std##Suffix##_scalar) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_ccm##Suffix##_scalar) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_std##Suffix##_rand_int) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_ccm##Suffix##_rand_int) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_std##Suffix##_rand_double) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_ccm##Suffix##_rand_double) CCMATH_BENCH_APPLY_RANGE; + +#define CCMATH_BENCH_DECLARE_BINARY_SUITE(Module, Name, Suffix, StdBody, CcmBody) \ + static void BM_##Module##_##Name##_std##Suffix##_scalar(benchmark::State& state) \ + { ccm::bench::binary_scalar(state, [&](double x, double y) { StdBody; }); } \ + static void BM_##Module##_##Name##_ccm##Suffix##_scalar(benchmark::State& state) \ + { ccm::bench::binary_scalar(state, [&](double x, double y) { CcmBody; }); } \ + static void BM_##Module##_##Name##_std##Suffix##_rand_int(benchmark::State& state) \ + { \ + auto xs = ccm::bench::random_integers(state.range(0)); \ + auto ys = ccm::bench::random_integers(state.range(0)); \ + ccm::bench::binary_vector(state, [&](int x, int y) { StdBody; }, xs, ys); \ + } \ + static void BM_##Module##_##Name##_ccm##Suffix##_rand_int(benchmark::State& state) \ + { \ + auto xs = ccm::bench::random_integers(state.range(0)); \ + auto ys = ccm::bench::random_integers(state.range(0)); \ + ccm::bench::binary_vector(state, [&](int x, int y) { CcmBody; }, xs, ys); \ + } \ + static void BM_##Module##_##Name##_std##Suffix##_rand_double(benchmark::State& state) \ + { \ + auto xs = ccm::bench::random_doubles(state.range(0)); \ + auto ys = ccm::bench::random_doubles(state.range(0)); \ + ccm::bench::binary_vector(state, [&](double x, double y) { StdBody; }, xs, ys); \ + } \ + static void BM_##Module##_##Name##_ccm##Suffix##_rand_double(benchmark::State& state) \ + { \ + auto xs = ccm::bench::random_doubles(state.range(0)); \ + auto ys = ccm::bench::random_doubles(state.range(0)); \ + ccm::bench::binary_vector(state, [&](double x, double y) { CcmBody; }, xs, ys); \ + } + +#define CCMATH_BENCH_REGISTER_BINARY_SUITE(Module, Name, Suffix) \ + BENCHMARK(BM_##Module##_##Name##_std##Suffix##_scalar) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_ccm##Suffix##_scalar) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_std##Suffix##_rand_int) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_ccm##Suffix##_rand_int) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_std##Suffix##_rand_double) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_ccm##Suffix##_rand_double) CCMATH_BENCH_APPLY_RANGE; + +#define CCMATH_BENCH_DECLARE_TERNARY_SUITE(Module, Name, Suffix, StdBody, CcmBody) \ + static void BM_##Module##_##Name##_std##Suffix##_scalar(benchmark::State& state) \ + { ccm::bench::ternary_scalar(state, [&](double x, double y, double z) { StdBody; }); } \ + static void BM_##Module##_##Name##_ccm##Suffix##_scalar(benchmark::State& state) \ + { ccm::bench::ternary_scalar(state, [&](double x, double y, double z) { CcmBody; }); } \ + static void BM_##Module##_##Name##_std##Suffix##_rand_int(benchmark::State& state) \ + { \ + auto xs = ccm::bench::random_integers(state.range(0)); \ + auto ys = ccm::bench::random_integers(state.range(0)); \ + auto zs = ccm::bench::random_integers(state.range(0)); \ + ccm::bench::ternary_vector(state, [&](int x, int y, int z) { StdBody; }, xs, ys, zs); \ + } \ + static void BM_##Module##_##Name##_ccm##Suffix##_rand_int(benchmark::State& state) \ + { \ + auto xs = ccm::bench::random_integers(state.range(0)); \ + auto ys = ccm::bench::random_integers(state.range(0)); \ + auto zs = ccm::bench::random_integers(state.range(0)); \ + ccm::bench::ternary_vector(state, [&](int x, int y, int z) { CcmBody; }, xs, ys, zs); \ + } \ + static void BM_##Module##_##Name##_std##Suffix##_rand_double(benchmark::State& state) \ + { \ + auto xs = ccm::bench::random_doubles(state.range(0)); \ + auto ys = ccm::bench::random_doubles(state.range(0)); \ + auto zs = ccm::bench::random_doubles(state.range(0)); \ + ccm::bench::ternary_vector(state, [&](double x, double y, double z) { StdBody; }, xs, ys, zs); \ + } \ + static void BM_##Module##_##Name##_ccm##Suffix##_rand_double(benchmark::State& state) \ + { \ + auto xs = ccm::bench::random_doubles(state.range(0)); \ + auto ys = ccm::bench::random_doubles(state.range(0)); \ + auto zs = ccm::bench::random_doubles(state.range(0)); \ + ccm::bench::ternary_vector(state, [&](double x, double y, double z) { CcmBody; }, xs, ys, zs); \ + } + +#define CCMATH_BENCH_REGISTER_TERNARY_SUITE(Module, Name, Suffix) \ + BENCHMARK(BM_##Module##_##Name##_std##Suffix##_scalar) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_ccm##Suffix##_scalar) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_std##Suffix##_rand_int) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_ccm##Suffix##_rand_int) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_std##Suffix##_rand_double) CCMATH_BENCH_APPLY_RANGE; \ + BENCHMARK(BM_##Module##_##Name##_ccm##Suffix##_rand_double) CCMATH_BENCH_APPLY_RANGE; + +#define CCMATH_BENCH_DECLARE_UNARY_PROFILE_SUITE(Module, Name, Suffix, Profile, Value, StdBody, CcmBody) \ + static void BM_##Module##_##Name##_std##Suffix##_##Profile(benchmark::State& state) \ + { ccm::bench::unary_fixed(state, Value, [&](double x) { StdBody; }); } \ + static void BM_##Module##_##Name##_ccm##Suffix##_##Profile(benchmark::State& state) \ + { ccm::bench::unary_fixed(state, Value, [&](double x) { CcmBody; }); } + +#define CCMATH_BENCH_REGISTER_UNARY_PROFILE_SUITE(Module, Name, Suffix, Profile) \ + BENCHMARK(BM_##Module##_##Name##_std##Suffix##_##Profile); \ + BENCHMARK(BM_##Module##_##Name##_ccm##Suffix##_##Profile); + +#define CCMATH_BENCH_DECLARE_BINARY_PROFILE_SUITE(Module, Name, Suffix, Profile, X, Y, StdBody, CcmBody) \ + static void BM_##Module##_##Name##_std##Suffix##_##Profile(benchmark::State& state) \ + { ccm::bench::binary_fixed(state, X, Y, [&](double x, double y) { StdBody; }); } \ + static void BM_##Module##_##Name##_ccm##Suffix##_##Profile(benchmark::State& state) \ + { ccm::bench::binary_fixed(state, X, Y, [&](double x, double y) { CcmBody; }); } + +#define CCMATH_BENCH_REGISTER_BINARY_PROFILE_SUITE(Module, Name, Suffix, Profile) \ + BENCHMARK(BM_##Module##_##Name##_std##Suffix##_##Profile); \ + BENCHMARK(BM_##Module##_##Name##_ccm##Suffix##_##Profile); + +#define CCMATH_BENCH_UNARY_COMPARE(Module, Name, StdBody, CcmBody) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_DECLARE_UNARY_SUITE(Module, Name, , StdBody, CcmBody)) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_REGISTER_UNARY_SUITE(Module, Name, )) + +#define CCMATH_BENCH_UNARY_COMPARE_PATHS(Module, Name, StdBody, CcmCtBody, CcmRtBody) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_DECLARE_UNARY_SUITE(Module, Name, , StdBody, CcmCtBody)) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_REGISTER_UNARY_SUITE(Module, Name, )) \ + CCMATH_BENCH_IF_RT(CCMATH_BENCH_DECLARE_UNARY_SUITE(Module, Name, _rt, StdBody, CcmRtBody)) \ + CCMATH_BENCH_IF_RT(CCMATH_BENCH_REGISTER_UNARY_SUITE(Module, Name, _rt)) + +#define CCMATH_BENCH_BINARY_COMPARE(Module, Name, StdBody, CcmBody) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_DECLARE_BINARY_SUITE(Module, Name, , StdBody, CcmBody)) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_REGISTER_BINARY_SUITE(Module, Name, )) + +#define CCMATH_BENCH_BINARY_COMPARE_PATHS(Module, Name, StdBody, CcmCtBody, CcmRtBody) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_DECLARE_BINARY_SUITE(Module, Name, , StdBody, CcmCtBody)) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_REGISTER_BINARY_SUITE(Module, Name, )) \ + CCMATH_BENCH_IF_RT(CCMATH_BENCH_DECLARE_BINARY_SUITE(Module, Name, _rt, StdBody, CcmRtBody)) \ + CCMATH_BENCH_IF_RT(CCMATH_BENCH_REGISTER_BINARY_SUITE(Module, Name, _rt)) + +#define CCMATH_BENCH_TERNARY_COMPARE(Module, Name, StdBody, CcmBody) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_DECLARE_TERNARY_SUITE(Module, Name, , StdBody, CcmBody)) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_REGISTER_TERNARY_SUITE(Module, Name, )) + +#define CCMATH_BENCH_TERNARY_COMPARE_PATHS(Module, Name, StdBody, CcmCtBody, CcmRtBody) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_DECLARE_TERNARY_SUITE(Module, Name, , StdBody, CcmCtBody)) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_REGISTER_TERNARY_SUITE(Module, Name, )) \ + CCMATH_BENCH_IF_RT(CCMATH_BENCH_DECLARE_TERNARY_SUITE(Module, Name, _rt, StdBody, CcmRtBody)) \ + CCMATH_BENCH_IF_RT(CCMATH_BENCH_REGISTER_TERNARY_SUITE(Module, Name, _rt)) + +#define CCMATH_BENCH_UNARY_PROFILE(Module, Name, Profile, Value, StdBody, CcmBody) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_DECLARE_UNARY_PROFILE_SUITE(Module, Name, , Profile, Value, StdBody, CcmBody)) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_REGISTER_UNARY_PROFILE_SUITE(Module, Name, , Profile)) + +#define CCMATH_BENCH_UNARY_PROFILE_PATHS(Module, Name, Profile, Value, StdBody, CcmCtBody, CcmRtBody) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_DECLARE_UNARY_PROFILE_SUITE(Module, Name, , Profile, Value, StdBody, CcmCtBody)) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_REGISTER_UNARY_PROFILE_SUITE(Module, Name, , Profile)) \ + CCMATH_BENCH_IF_RT(CCMATH_BENCH_DECLARE_UNARY_PROFILE_SUITE(Module, Name, _rt, Profile, Value, StdBody, CcmRtBody)) \ + CCMATH_BENCH_IF_RT(CCMATH_BENCH_REGISTER_UNARY_PROFILE_SUITE(Module, Name, _rt, Profile)) + +#define CCMATH_BENCH_BINARY_PROFILE(Module, Name, Profile, X, Y, StdBody, CcmBody) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_DECLARE_BINARY_PROFILE_SUITE(Module, Name, , Profile, X, Y, StdBody, CcmBody)) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_REGISTER_BINARY_PROFILE_SUITE(Module, Name, , Profile)) + +#define CCMATH_BENCH_BINARY_PROFILE_PATHS(Module, Name, Profile, X, Y, StdBody, CcmCtBody, CcmRtBody) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_DECLARE_BINARY_PROFILE_SUITE(Module, Name, , Profile, X, Y, StdBody, CcmCtBody)) \ + CCMATH_BENCH_IF_CT(CCMATH_BENCH_REGISTER_BINARY_PROFILE_SUITE(Module, Name, , Profile)) \ + CCMATH_BENCH_IF_RT(CCMATH_BENCH_DECLARE_BINARY_PROFILE_SUITE(Module, Name, _rt, Profile, X, Y, StdBody, CcmRtBody)) \ + CCMATH_BENCH_IF_RT(CCMATH_BENCH_REGISTER_BINARY_PROFILE_SUITE(Module, Name, _rt, Profile)) + +#define CCMATH_BENCH_DECLARE_UNARY_COMPARE(Module, Name, StdBody, CcmBody) \ + CCMATH_BENCH_DECLARE_UNARY_SUITE(Module, Name, , StdBody, CcmBody) + +#define CCMATH_BENCH_REGISTER_UNARY_COMPARE(Module, Name) CCMATH_BENCH_REGISTER_UNARY_SUITE(Module, Name, ) + +#define CCMATH_BENCH_DECLARE_BINARY_COMPARE(Module, Name, StdBody, CcmBody) \ + CCMATH_BENCH_DECLARE_BINARY_SUITE(Module, Name, , StdBody, CcmBody) + +#define CCMATH_BENCH_REGISTER_BINARY_COMPARE(Module, Name) CCMATH_BENCH_REGISTER_BINARY_SUITE(Module, Name, ) + +#define CCMATH_BENCH_DECLARE_TERNARY_COMPARE(Module, Name, StdBody, CcmBody) \ + CCMATH_BENCH_DECLARE_TERNARY_SUITE(Module, Name, , StdBody, CcmBody) + +#define CCMATH_BENCH_REGISTER_TERNARY_COMPARE(Module, Name) CCMATH_BENCH_REGISTER_TERNARY_SUITE(Module, Name, ) diff --git a/benchmarks/src/math/basic/abs.bench.cpp b/benchmarks/src/math/basic/abs.bench.cpp new file mode 100644 index 00000000..23a14e42 --- /dev/null +++ b/benchmarks/src/math/basic/abs.bench.cpp @@ -0,0 +1,9 @@ +#include "shared/register.hpp" + +#include + +#include + +CCMATH_BENCH_UNARY_COMPARE(basic, abs, return std::abs(x);, return ccm::abs(x);) + +BENCHMARK_MAIN(); diff --git a/benchmarks/src/math/basic/fdim.bench.cpp b/benchmarks/src/math/basic/fdim.bench.cpp new file mode 100644 index 00000000..be342109 --- /dev/null +++ b/benchmarks/src/math/basic/fdim.bench.cpp @@ -0,0 +1,9 @@ +#include "shared/register.hpp" + +#include + +#include + +CCMATH_BENCH_BINARY_COMPARE(basic, fdim, return std::fdim(x, y);, return ccm::fdim(x, y);) + +BENCHMARK_MAIN(); diff --git a/benchmarks/src/math/basic/fma.bench.cpp b/benchmarks/src/math/basic/fma.bench.cpp new file mode 100644 index 00000000..ed019cf0 --- /dev/null +++ b/benchmarks/src/math/basic/fma.bench.cpp @@ -0,0 +1,9 @@ +#include "shared/register.hpp" + +#include + +#include + +CCMATH_BENCH_TERNARY_COMPARE(basic, fma, return std::fma(x, y, z);, return ccm::fma(x, y, z);) + +BENCHMARK_MAIN(); diff --git a/benchmarks/src/math/nearest/trunc.bench.cpp b/benchmarks/src/math/nearest/trunc.bench.cpp new file mode 100644 index 00000000..0359307b --- /dev/null +++ b/benchmarks/src/math/nearest/trunc.bench.cpp @@ -0,0 +1,9 @@ +#include "shared/register.hpp" + +#include + +#include + +CCMATH_BENCH_UNARY_COMPARE(nearest, trunc, return std::trunc(x);, return ccm::trunc(x);) + +BENCHMARK_MAIN(); diff --git a/benchmarks/src/math/power/sqrt.bench.cpp b/benchmarks/src/math/power/sqrt.bench.cpp new file mode 100644 index 00000000..1aacbf1c --- /dev/null +++ b/benchmarks/src/math/power/sqrt.bench.cpp @@ -0,0 +1,23 @@ +#include "shared/register.hpp" + +#include + +#include + +namespace +{ + inline double sqrt_rt(double v) + { return ccm::sqrt(v); } +} // namespace + +CCMATH_BENCH_UNARY_COMPARE_PATHS(power, sqrt, + return std::sqrt(static_cast(x)), + return ccm::sqrt(static_cast(x)), + return sqrt_rt(static_cast(x))) + +CCMATH_BENCH_UNARY_PROFILE_PATHS(power, sqrt, positive_finite_general, 4.0, + return std::sqrt(x), + return ccm::sqrt(x), + return sqrt_rt(x)) + +BENCHMARK_MAIN(); diff --git a/benchmarks/tools/plot.py b/benchmarks/tools/plot.py deleted file mode 100644 index 78faaeab..00000000 --- a/benchmarks/tools/plot.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env python -"""Script to visualize google-benchmark output""" -import argparse -import sys -import logging -import json -import pandas as pd -import matplotlib.pyplot as plt -import pathlib - -# Borrowed from: https://github.com/lakshayg/google_benchmark_plot/blob/master/plot.py - -logging.basicConfig(format="[%(levelname)s] %(message)s") - -METRICS = [ - "real_time", - "cpu_time", - "bytes_per_second", - "items_per_second", - "iterations", -] -TRANSFORMS = {"": lambda x: x, "inverse": lambda x: 1.0 / x} - - -def get_default_ylabel(args): - """Compute default ylabel for commandline args""" - label = "" - if args.transform == "": - label = args.metric - else: - label = args.transform + "(" + args.metric + ")" - if args.relative_to is not None: - label += " relative to %s" % args.relative_to - return label - - -def parse_args(): - """Parse commandline arguments""" - parser = argparse.ArgumentParser(description="Visualize google-benchmark output") - parser.add_argument( - "-f", - metavar="FILE", - type=argparse.FileType("r"), - default=sys.stdin, - dest="file", - help="path to file containing the csv or json benchmark data", - ) - parser.add_argument( - "-m", - metavar="METRIC", - choices=METRICS, - default=METRICS[0], - dest="metric", - help="metric to plot on the y-axis, valid choices are: %s" % ", ".join(METRICS), - ) - parser.add_argument( - "-t", - metavar="TRANSFORM", - choices=TRANSFORMS.keys(), - default="", - help="transform to apply to the chosen metric, valid choices are: %s" - % ", ".join(list(TRANSFORMS)), - dest="transform", - ) - parser.add_argument( - "-r", - metavar="RELATIVE_TO", - type=str, - default=None, - dest="relative_to", - help="plot metrics relative to this label", - ) - parser.add_argument( - "--xlabel", type=str, default="input size", help="label of the x-axis" - ) - parser.add_argument("--ylabel", type=str, help="label of the y-axis") - parser.add_argument("--title", type=str, default="", help="title of the plot") - parser.add_argument( - "--logx", action="store_true", help="plot x-axis on a logarithmic scale" - ) - parser.add_argument( - "--logy", action="store_true", help="plot y-axis on a logarithmic scale" - ) - parser.add_argument( - "--output", type=str, default="", help="File in which to save the graph" - ) - - args = parser.parse_args() - if args.ylabel is None: - args.ylabel = get_default_ylabel(args) - return args - - -def parse_input_size(name): - splits = name.split("/") - if len(splits) == 1: - return 1 - return int(splits[1]) - - -def read_data(args): - """Read and process dataframe using commandline args""" - extension = pathlib.Path(args.file.name).suffix - try: - if extension == ".csv": - data = pd.read_csv(args.file, usecols=["name", args.metric]) - elif extension == ".json": - json_data = json.load(args.file) - data = pd.DataFrame(json_data["benchmarks"]) - else: - logging.error("Unsupported file extension '{}'".format(extension)) - exit(1) - except ValueError: - logging.error( - 'Could not parse the benchmark data. Did you forget "--benchmark_format=[csv|json] when running the benchmark"?' - ) - exit(1) - data["label"] = data["name"].apply(lambda x: x.split("/")[0]) - data["input"] = data["name"].apply(parse_input_size) - data[args.metric] = data[args.metric].apply(TRANSFORMS[args.transform]) - return data - - -def plot_groups(label_groups, args): - """Display the processed data""" - for label, group in label_groups.items(): - plt.plot(group["input"], group[args.metric], label=label, marker=".") - if args.logx: - plt.xscale("log") - if args.logy: - plt.yscale("log") - plt.xlabel(args.xlabel) - plt.ylabel(args.ylabel) - plt.title(args.title) - plt.legend() - if args.output: - logging.info("Saving to %s" % args.output) - plt.savefig(args.output) - else: - plt.show() - - -def main(): - """Entry point of the program""" - args = parse_args() - data = read_data(args) - label_groups = {} - for label, group in data.groupby("label"): - label_groups[label] = group.set_index("input", drop=False) - if args.relative_to is not None: - try: - baseline = label_groups[args.relative_to][args.metric].copy() - except KeyError as key: - msg = "Key %s is not present in the benchmark output" - logging.error(msg, str(key)) - exit(1) - - if args.relative_to is not None: - for label in label_groups: - label_groups[label][args.metric] /= baseline - plot_groups(label_groups, args) - - -if __name__ == "__main__": - main() diff --git a/cmake/README.md b/cmake/README.md new file mode 100644 index 00000000..f5d14783 --- /dev/null +++ b/cmake/README.md @@ -0,0 +1,41 @@ +# CMake tooling + +Helpers in cmake/helpers/ and module lists in cmake/config/. Family CMakeLists.txt files include a registry and call ccmath_add_* helpers instead of listing every source file. + +## Registries + +| Registry | Helper | Wired from | +| --- | --- | --- | +| TestModuleRegistry.cmake | CcmTestHelpers.cmake | tests/unit/CMakeLists.txt | +| OracleCampaignRegistry.cmake | CcmOracleHelpers.cmake | tests/rigorous/CMakeLists.txt | +| FuzzModuleRegistry.cmake | CcmFuzzHelpers.cmake | fuzzing/CMakeLists.txt | +| BenchmarkModuleRegistry.cmake | CcmBenchHelpers.cmake | benchmarks/CMakeLists.txt | +| ExampleModuleRegistry.cmake | CcmExampleHelpers.cmake | examples/CMakeLists.txt | + +TestCtestTargets.cmake defines ccmath-ctest-simple and ccmath-ctest-rigorous. TestOracleTargets.cmake links MPFR and CORE-MATH. cmake/modules/CcmLibFuzzer.cmake probes libFuzzer. + +## Family docs + +| Area | README | +| --- | --- | +| GTest suites | tests/README.md | +| Rigorous wiring | tests/rigorous/README.md | +| Proof smoke | tests/proofs/README.md | +| Oracle campaigns | tests/shared/oracle/README.md | +| Fuzz | fuzzing/README.md | +| Benchmarks | benchmarks/README.md | +| Examples | examples/README.md | + +## Helpers (quick ref) + +CcmTestHelpers.cmake needs CCMATH_TESTS_ROOT set first (tests/CMakeLists.txt does this). ccmath_add_unit_module globs tests/src/math//. ccmath_add_conformance_suite and ccmath_add_rigorous_module_suite cover rigorous GTest subsets. + +CcmFuzzHelpers.cmake needs CCMATH_FUZZ_ROOT. ccmath_add_fuzz_module builds the executable, seeds, smoke CTest, and campaign target. + +CcmBenchHelpers.cmake needs CCMATH_BENCH_ROOT. ccmath_apply_bench_registry loops BenchmarkModuleRegistry.cmake. + +CcmExampleHelpers.cmake needs CCMATH_EXAMPLE_ROOT. ccmath_apply_example_registry loops ExampleModuleRegistry.cmake. + +CcmOracleHelpers.cmake: ccmath_apply_oracle_registries(LOG_DIR) reads OracleCampaignRegistry.cmake. + +Dedicated targets stay in the family CMakeLists.txt when a source file needs its own COMPILE_DEFINITIONS or must be excluded from a glob. diff --git a/cmake/config/BenchmarkModuleRegistry.cmake b/cmake/config/BenchmarkModuleRegistry.cmake new file mode 100644 index 00000000..10fe5ac5 --- /dev/null +++ b/cmake/config/BenchmarkModuleRegistry.cmake @@ -0,0 +1,16 @@ +# Declarative benchmark registry. +# Add a function: drop benchmarks/src/math//.bench.cpp and append below. + +set(CCMATH_BENCH_MODULE_ORDER basic power nearest compare) + +set(CCMATH_BENCH_MODULE_basic_FUNCTIONS abs fdim fma) +set(CCMATH_BENCH_MODULE_basic_OPTION CCM_BENCH_BASIC) + +set(CCMATH_BENCH_MODULE_power_FUNCTIONS sqrt) +set(CCMATH_BENCH_MODULE_power_OPTION CCM_BENCH_POWER) + +set(CCMATH_BENCH_MODULE_nearest_FUNCTIONS trunc) +set(CCMATH_BENCH_MODULE_nearest_OPTION CCM_BENCH_NEAREST) + +set(CCMATH_BENCH_MODULE_compare_FUNCTIONS) +set(CCMATH_BENCH_MODULE_compare_OPTION CCM_BENCH_COMPARE) diff --git a/cmake/config/BuildManifest.cmake b/cmake/config/BuildManifest.cmake new file mode 100644 index 00000000..faa50673 --- /dev/null +++ b/cmake/config/BuildManifest.cmake @@ -0,0 +1,114 @@ +# Library-facing build metadata for CMake, manifest emission, and secondary build codegen. + +set(CCMATH_LIBRARY_MANIFEST_CPP_STANDARD 17) + +set(CCMATH_LIBRARY_MANIFEST_GENERATED_HEADERS + ccmath/version.hpp +) + +set(CCMATH_LIBRARY_MANIFEST_VERSION_TEMPLATE cmake/in/version.hpp.in) + +# Ordered option keys. Each key sets CCMATH_LIBRARY_MANIFEST_OPTION__* variables. +set(CCMATH_LIBRARY_MANIFEST_OPTION_KEYS + runtime_simd + disable_errno + reduced_precision_powl + deterministic +) + +set(CCMATH_LIBRARY_MANIFEST_OPTION_runtime_simd_CMAKE_VAR CCMATH_DISABLE_RUNTIME_SIMD) +set(CCMATH_LIBRARY_MANIFEST_OPTION_runtime_simd_CMAKE_DEFAULT OFF) +set(CCMATH_LIBRARY_MANIFEST_OPTION_runtime_simd_CMAKE_INVERT TRUE) +set(CCMATH_LIBRARY_MANIFEST_OPTION_runtime_simd_MESON_OPTION enable_runtime_simd) +set(CCMATH_LIBRARY_MANIFEST_OPTION_runtime_simd_DEFINE CCM_CONFIG_USE_RT_SIMD) +set(CCMATH_LIBRARY_MANIFEST_OPTION_runtime_simd_DESCRIPTION + "Enable SIMD optimization for runtime evaluation (does not affect compile-time)") + +set(CCMATH_LIBRARY_MANIFEST_OPTION_disable_errno_CMAKE_VAR CCMATH_DISABLE_ERRNO) +set(CCMATH_LIBRARY_MANIFEST_OPTION_disable_errno_CMAKE_DEFAULT OFF) +set(CCMATH_LIBRARY_MANIFEST_OPTION_disable_errno_CMAKE_INVERT FALSE) +set(CCMATH_LIBRARY_MANIFEST_OPTION_disable_errno_MESON_OPTION disable_errno) +set(CCMATH_LIBRARY_MANIFEST_OPTION_disable_errno_DEFINE CCM_CONFIG_DISABLE_ERRNO) +set(CCMATH_LIBRARY_MANIFEST_OPTION_disable_errno_DESCRIPTION + "Disable the use of errno in ccmath during runtime (may lead to faster evaluations but is non-standard)") + +set(CCMATH_LIBRARY_MANIFEST_OPTION_reduced_precision_powl_CMAKE_VAR CCMATH_DISABLE_REDUCED_PRECISION_POWL) +set(CCMATH_LIBRARY_MANIFEST_OPTION_reduced_precision_powl_CMAKE_DEFAULT OFF) +set(CCMATH_LIBRARY_MANIFEST_OPTION_reduced_precision_powl_CMAKE_INVERT FALSE) +set(CCMATH_LIBRARY_MANIFEST_OPTION_reduced_precision_powl_MESON_OPTION disable_reduced_precision_powl) +set(CCMATH_LIBRARY_MANIFEST_OPTION_reduced_precision_powl_DEFINE CCM_CONFIG_DISABLE_REDUCED_PRECISION_POWL) +set(CCMATH_LIBRARY_MANIFEST_OPTION_reduced_precision_powl_DESCRIPTION + "Return quiet NaN from powl on non-binary80 long double instead of the default reduced-precision double fallback") + +set(CCMATH_LIBRARY_MANIFEST_OPTION_deterministic_CMAKE_VAR CCMATH_ENABLE_DETERMINISTIC) +set(CCMATH_LIBRARY_MANIFEST_OPTION_deterministic_CMAKE_DEFAULT OFF) +set(CCMATH_LIBRARY_MANIFEST_OPTION_deterministic_CMAKE_INVERT FALSE) +set(CCMATH_LIBRARY_MANIFEST_OPTION_deterministic_MESON_OPTION deterministic) +set(CCMATH_LIBRARY_MANIFEST_OPTION_deterministic_DEFINE CCM_CONFIG_DETERMINISTIC) +set(CCMATH_LIBRARY_MANIFEST_OPTION_deterministic_DESCRIPTION + "Produce bit-identical cross-hardware math: route transcendentals through the generic kernels (no libm), force the correctly-rounded FMA path, disable runtime SIMD, and evaluate long double in double precision (GCC/Clang)") + +function(ccmath_manifest_declare_library_options) + foreach (_ccmath_manifest_key IN LISTS CCMATH_LIBRARY_MANIFEST_OPTION_KEYS) + set(_ccmath_cmake_var "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_CMAKE_VAR}") + set(_ccmath_description "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_DESCRIPTION}") + set(_ccmath_default "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_CMAKE_DEFAULT}") + option("${_ccmath_cmake_var}" "${_ccmath_description}" "${_ccmath_default}") + endforeach () +endfunction() + +function(ccmath_manifest_apply_library_compile_definitions target) + foreach (_ccmath_manifest_key IN LISTS CCMATH_LIBRARY_MANIFEST_OPTION_KEYS) + set(_ccmath_cmake_var "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_CMAKE_VAR}") + set(_ccmath_define "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_DEFINE}") + set(_ccmath_invert "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_CMAKE_INVERT}") + + if (_ccmath_invert) + if (NOT ${_ccmath_cmake_var}) + target_compile_definitions("${target}" INTERFACE "${_ccmath_define}") + endif () + else () + if (${_ccmath_cmake_var}) + target_compile_definitions("${target}" INTERFACE "${_ccmath_define}") + endif () + endif () + endforeach () +endfunction() + +function(ccmath_manifest_meson_default option_key) + set(_ccmath_cmake_default "${CCMATH_LIBRARY_MANIFEST_OPTION_${option_key}_CMAKE_DEFAULT}") + set(_ccmath_invert "${CCMATH_LIBRARY_MANIFEST_OPTION_${option_key}_CMAKE_INVERT}") + + if (_ccmath_invert) + if (_ccmath_cmake_default) + set(_ccmath_meson_default false PARENT_SCOPE) + else () + set(_ccmath_meson_default true PARENT_SCOPE) + endif () + else () + if (_ccmath_cmake_default) + set(_ccmath_meson_default true PARENT_SCOPE) + else () + set(_ccmath_meson_default false PARENT_SCOPE) + endif () + endif () +endfunction() + +function(ccmath_manifest_option_enabled option_key out_var) + set(_ccmath_cmake_var "${CCMATH_LIBRARY_MANIFEST_OPTION_${option_key}_CMAKE_VAR}") + set(_ccmath_invert "${CCMATH_LIBRARY_MANIFEST_OPTION_${option_key}_CMAKE_INVERT}") + + if (_ccmath_invert) + if (NOT ${_ccmath_cmake_var}) + set("${out_var}" TRUE PARENT_SCOPE) + else () + set("${out_var}" FALSE PARENT_SCOPE) + endif () + else () + if (${_ccmath_cmake_var}) + set("${out_var}" TRUE PARENT_SCOPE) + else () + set("${out_var}" FALSE PARENT_SCOPE) + endif () + endif () +endfunction() diff --git a/cmake/config/EmitBuildManifest.cmake b/cmake/config/EmitBuildManifest.cmake new file mode 100644 index 00000000..83d87932 --- /dev/null +++ b/cmake/config/EmitBuildManifest.cmake @@ -0,0 +1,148 @@ +include_guard(GLOBAL) + +include("${CMAKE_CURRENT_LIST_DIR}/BuildManifest.cmake") + +function(_ccmath_manifest_json_escape input output_var) + string(REPLACE "\\" "\\\\" _ccmath_escaped "${input}") + string(REPLACE "\"" "\\\"" _ccmath_escaped "${_ccmath_escaped}") + set("${output_var}" "${_ccmath_escaped}" PARENT_SCOPE) +endfunction() + +function(_ccmath_manifest_append_option_json_lines out_var) + set(_ccmath_json_lines "") + set(_ccmath_comma "") + + foreach (_ccmath_manifest_key IN LISTS CCMATH_LIBRARY_MANIFEST_OPTION_KEYS) + set(_ccmath_cmake_var "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_CMAKE_VAR}") + set(_ccmath_meson_option "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_MESON_OPTION}") + set(_ccmath_define "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_DEFINE}") + set(_ccmath_description "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_DESCRIPTION}") + set(_ccmath_cmake_default "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_CMAKE_DEFAULT}") + set(_ccmath_invert "${CCMATH_LIBRARY_MANIFEST_OPTION_${_ccmath_manifest_key}_CMAKE_INVERT}") + + ccmath_manifest_meson_default("${_ccmath_manifest_key}" _ccmath_meson_default) + ccmath_manifest_option_enabled("${_ccmath_manifest_key}" _ccmath_enabled) + + _ccmath_manifest_json_escape("${_ccmath_description}" _ccmath_description_json) + + if (_ccmath_cmake_default) + set(_ccmath_cmake_default_json "true") + else () + set(_ccmath_cmake_default_json "false") + endif () + + if (_ccmath_meson_default) + set(_ccmath_meson_default_json "true") + else () + set(_ccmath_meson_default_json "false") + endif () + + if (_ccmath_invert) + set(_ccmath_invert_json "true") + else () + set(_ccmath_invert_json "false") + endif () + + if (_ccmath_enabled) + set(_ccmath_enabled_json "true") + else () + set(_ccmath_enabled_json "false") + endif () + + string(APPEND _ccmath_json_lines + "${_ccmath_comma} {\n" + " \"key\": \"${_ccmath_manifest_key}\",\n" + " \"cmake_option\": \"${_ccmath_cmake_var}\",\n" + " \"meson_option\": \"${_ccmath_meson_option}\",\n" + " \"define\": \"${_ccmath_define}\",\n" + " \"description\": \"${_ccmath_description_json}\",\n" + " \"cmake_default\": ${_ccmath_cmake_default_json},\n" + " \"meson_default\": ${_ccmath_meson_default_json},\n" + " \"cmake_invert\": ${_ccmath_invert_json},\n" + " \"enabled\": ${_ccmath_enabled_json}\n" + " }") + set(_ccmath_comma ",\n") + endforeach () + + set("${out_var}" "${_ccmath_json_lines}" PARENT_SCOPE) +endfunction() + +function(ccmath_emit_build_manifest output_path) + _ccmath_manifest_append_option_json_lines(_ccmath_option_json) + + set(_ccmath_generated_headers_json "") + set(_ccmath_header_comma "") + foreach (_ccmath_header IN LISTS CCMATH_LIBRARY_MANIFEST_GENERATED_HEADERS) + string(APPEND _ccmath_generated_headers_json + "${_ccmath_header_comma} \"${_ccmath_header}\"") + set(_ccmath_header_comma ",\n") + endforeach () + + _ccmath_manifest_json_escape("${CMAKE_CURRENT_SOURCE_DIR}" _ccmath_source_dir_json) + _ccmath_manifest_json_escape("${CMAKE_CURRENT_BINARY_DIR}" _ccmath_build_dir_json) + _ccmath_manifest_json_escape("${CCMATH_LIBRARY_MANIFEST_VERSION_TEMPLATE}" _ccmath_version_template_json) + + set(_ccmath_manifest_json "") + string(APPEND _ccmath_manifest_json + "{\n" + " \"name\": \"${CCMATH_TARGET_NAME}\",\n" + " \"version\": \"${CCMATH_BUILD_VERSION}\",\n" + " \"cpp_standard\": ${CCMATH_LIBRARY_MANIFEST_CPP_STANDARD},\n" + " \"source_dir\": \"${_ccmath_source_dir_json}\",\n" + " \"build_dir\": \"${_ccmath_build_dir_json}\",\n" + " \"include_dirs\": [\n" + " \"include\",\n" + " \"${CMAKE_CURRENT_BINARY_DIR}/include\"\n" + " ],\n" + " \"version_template\": \"${_ccmath_version_template_json}\",\n" + " \"generated_headers\": [\n" + "${_ccmath_generated_headers_json}\n" + " ],\n" + " \"options\": [\n" + "${_ccmath_option_json}\n" + " ]\n" + "}\n") + + file(WRITE "${output_path}" "${_ccmath_manifest_json}") +endfunction() + +function(ccmath_setup_secondary_build_generation) + set(_ccmath_manifest_path "${CMAKE_CURRENT_BINARY_DIR}/ccmath-build-manifest.json") + set(_ccmath_gen_dir "${CMAKE_CURRENT_BINARY_DIR}/gen-secondary") + + ccmath_emit_build_manifest("${_ccmath_manifest_path}") + + find_package(Python3 COMPONENTS Interpreter QUIET) + if (NOT Python3_Interpreter_FOUND) + message(WARNING "CCMath: Python3 not found. Secondary build generation will be skipped.") + return() + endif () + + add_custom_command( + OUTPUT + "${_ccmath_gen_dir}/meson.build" + "${_ccmath_gen_dir}/meson_options.txt" + "${_ccmath_gen_dir}/premake5.lua" + "${_ccmath_gen_dir}/BUILD_STAMP" + COMMAND ${CMAKE_COMMAND} -E make_directory "${_ccmath_gen_dir}" + COMMAND ${CMAKE_COMMAND} -E copy "${_ccmath_manifest_path}" "${_ccmath_gen_dir}/ccmath-build-manifest.json" + COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/tools/gen_secondary_builds.py" + --manifest "${_ccmath_manifest_path}" + --output-dir "${_ccmath_gen_dir}" + --source-dir "${CMAKE_CURRENT_SOURCE_DIR}" + --update-root + COMMAND ${CMAKE_COMMAND} -E touch "${_ccmath_gen_dir}/BUILD_STAMP" + DEPENDS + "${_ccmath_manifest_path}" + "${CMAKE_CURRENT_SOURCE_DIR}/tools/gen_secondary_builds.py" + COMMENT "Generating Meson and Premake secondary build files" + VERBATIM + ) + + add_custom_target( + ccmath-generate-secondary-builds + ALL + DEPENDS + "${_ccmath_gen_dir}/BUILD_STAMP" + ) +endfunction() diff --git a/cmake/config/ExampleModuleRegistry.cmake b/cmake/config/ExampleModuleRegistry.cmake new file mode 100644 index 00000000..d2990af5 --- /dev/null +++ b/cmake/config/ExampleModuleRegistry.cmake @@ -0,0 +1,12 @@ +# Declarative example registry. +# Add an example: drop examples/src/.cpp and append below. + +set(CCMATH_EXAMPLE_MODULE_ORDER smoke basic trig power expo nearest compare) + +set(CCMATH_EXAMPLE_MODULE_smoke_EXAMPLES constexpr_smoke) +set(CCMATH_EXAMPLE_MODULE_basic_EXAMPLES constexpr_abs signed_magnitude) +set(CCMATH_EXAMPLE_MODULE_trig_EXAMPLES unit_circle) +set(CCMATH_EXAMPLE_MODULE_power_EXAMPLES hypot_distance) +set(CCMATH_EXAMPLE_MODULE_expo_EXAMPLES decibel_gain) +set(CCMATH_EXAMPLE_MODULE_nearest_EXAMPLES rounding_edge_cases) +set(CCMATH_EXAMPLE_MODULE_compare_EXAMPLES fp_classes) diff --git a/cmake/config/FindCoreMath.cmake b/cmake/config/FindCoreMath.cmake new file mode 100644 index 00000000..870eaf4c --- /dev/null +++ b/cmake/config/FindCoreMath.cmake @@ -0,0 +1,84 @@ +# Discover CORE-MATH oracle library for rigorous validation. +# +# Resolution order: +# 1. pkg-config module coremath or crmath +# 2. CCMATH_COREMATH_ROOT or CMAKE_PREFIX_PATH (crmath.h + libcoremath_oracle.a / libcrmath.a) +# 3. CCMATH_FETCH_COREMATH=ON FetchContent fallback + +include_guard(GLOBAL) + +set(CCMATH_COREMATH_FOUND FALSE) + +if (CCMATH_COREMATH_ROOT) + list(APPEND CMAKE_PREFIX_PATH "${CCMATH_COREMATH_ROOT}") +endif () + +find_package(PkgConfig QUIET) +if (PkgConfig_FOUND) + foreach (_pc_module IN ITEMS coremath crmath ccmath_coremath_oracle) + pkg_check_modules(_COREMATH_PC QUIET IMPORTED_TARGET ${_pc_module}) + if (TARGET PkgConfig::_COREMATH_PC) + if (NOT TARGET ccmath::coremath_oracle) + add_library(ccmath::coremath_oracle ALIAS PkgConfig::_COREMATH_PC) + endif () + set(CCMATH_COREMATH_FOUND TRUE) + break() + endif () + unset(_COREMATH_PC_LIBRARIES) + unset(_COREMATH_PC_INCLUDE_DIRS) + unset(_COREMATH_PC_CFLAGS) + unset(_COREMATH_PC_CFLAGS_OTHER) + endforeach () +endif () + +if (NOT CCMATH_COREMATH_FOUND) + find_path(CCMATH_COREMATH_INCLUDE_DIR + NAMES crmath.h coremath.h + HINTS ${CCMATH_COREMATH_ROOT} + PATH_SUFFIXES include + ) + find_library(CCMATH_COREMATH_LIBRARY + NAMES coremath_oracle crmath coremath + HINTS ${CCMATH_COREMATH_ROOT} + PATH_SUFFIXES lib lib64 + ) + + if (CCMATH_COREMATH_INCLUDE_DIR AND CCMATH_COREMATH_LIBRARY) + if (NOT TARGET ccmath_coremath_oracle) + add_library(ccmath_coremath_oracle UNKNOWN IMPORTED GLOBAL) + set_target_properties(ccmath_coremath_oracle PROPERTIES + IMPORTED_LOCATION "${CCMATH_COREMATH_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${CCMATH_COREMATH_INCLUDE_DIR}" + ) + add_library(ccmath::coremath_oracle ALIAS ccmath_coremath_oracle) + endif () + set(CCMATH_COREMATH_FOUND TRUE) + endif () +endif () + +if (NOT CCMATH_COREMATH_FOUND AND CCMATH_FETCH_COREMATH) + include(FetchContent) + FetchContent_Declare( + coremath_upstream + GIT_REPOSITORY https://github.com/mockingbirdnest/core-math.git + GIT_TAG master + GIT_SHALLOW TRUE + ) + FetchContent_MakeAvailable(coremath_upstream) + + set(CORE_MATH_SOURCE_DIR "${coremath_upstream_SOURCE_DIR}" CACHE PATH "" FORCE) + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../coremath ${CMAKE_BINARY_DIR}/_deps/coremath_oracle_build EXCLUDE_FROM_ALL) + + if (TARGET coremath_oracle AND NOT TARGET ccmath::coremath_oracle) + add_library(ccmath::coremath_oracle ALIAS coremath_oracle) + set(CCMATH_COREMATH_FOUND TRUE) + endif () +endif () + +if (CCMATH_ENABLE_COREMATH_TESTS AND NOT CCMATH_COREMATH_FOUND) + if (CCMATH_FETCH_COREMATH) + message(STATUS "CCMath rigorous CORE-MATH validation requested, but CORE-MATH was not found and FetchContent did not produce a target") + else () + message(STATUS "CCMath rigorous CORE-MATH validation requested, but CORE-MATH was not found (set CCMATH_COREMATH_ROOT or CCMATH_FETCH_COREMATH=ON)") + endif () +endif () diff --git a/cmake/config/FindMpfrGmp.cmake b/cmake/config/FindMpfrGmp.cmake new file mode 100644 index 00000000..23c4cdda --- /dev/null +++ b/cmake/config/FindMpfrGmp.cmake @@ -0,0 +1,62 @@ +# Discover MPFR and GMP for rigorous oracle validation. +# +# Linux and macOS typically use system pkg-config modules. +# Windows CI uses vcpkg (see cmake/vcpkg/vcpkg.json) with pkgconf and .pc files. + +include_guard(GLOBAL) + +set(CCMATH_MPFR_FOUND FALSE) + +# vcpkg pkg-config wiring is Windows-only. This is a function (not a macro) so the early +# returns scope to the helper instead of aborting the whole module on Linux and macOS. +function(_ccmath_configure_vcpkg_pkgconfig) + if (NOT WIN32) + return() + endif () + if (NOT CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg") + return() + endif () + if (NOT DEFINED VCPKG_INSTALLED_DIR OR NOT DEFINED VCPKG_TARGET_TRIPLET) + return() + endif () + + set(_vcpkg_triplet_root "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}") + find_program(CCMATH_PKGCONFIG_EXECUTABLE + NAMES pkgconf pkg-config + PATHS + "${_vcpkg_triplet_root}/tools/pkgconf" + "${_vcpkg_triplet_root}/tools/pkg-config" + PATH_SUFFIXES bin + NO_DEFAULT_PATH + ) + if (CCMATH_PKGCONFIG_EXECUTABLE) + set(PKG_CONFIG_EXECUTABLE "${CCMATH_PKGCONFIG_EXECUTABLE}" CACHE FILEPATH "" FORCE) + endif () + + set(ENV{PKG_CONFIG_PATH} + "${_vcpkg_triplet_root}/lib/pkgconfig;${_vcpkg_triplet_root}/share/pkgconfig") +endfunction() + +_ccmath_configure_vcpkg_pkgconfig() + +find_package(PkgConfig QUIET) +if (PkgConfig_FOUND) + pkg_check_modules(MPFR QUIET IMPORTED_TARGET mpfr) + pkg_check_modules(GMP QUIET IMPORTED_TARGET gmp) +endif () + +if (TARGET PkgConfig::MPFR AND TARGET PkgConfig::GMP) + if (NOT TARGET ccmath_mpfr_oracle) + add_library(ccmath_mpfr_oracle INTERFACE IMPORTED GLOBAL) + target_link_libraries(ccmath_mpfr_oracle INTERFACE PkgConfig::MPFR PkgConfig::GMP) + add_library(ccmath::mpfr_oracle ALIAS ccmath_mpfr_oracle) + endif () + set(CCMATH_MPFR_FOUND TRUE) +endif () + +if (CCMATH_ENABLE_MPFR_TESTS AND NOT CCMATH_MPFR_FOUND) + # Fail loudly: silently dropping the requested oracle suite lets CI pass without validation. + message(FATAL_ERROR + "CCMATH_ENABLE_MPFR_TESTS=ON but MPFR/GMP were not both found via pkg-config. " + "Install mpfr and gmp (and pkg-config), or set CCMATH_ENABLE_MPFR_TESTS=OFF.") +endif () diff --git a/cmake/config/FuzzModuleRegistry.cmake b/cmake/config/FuzzModuleRegistry.cmake new file mode 100644 index 00000000..133d082d --- /dev/null +++ b/cmake/config/FuzzModuleRegistry.cmake @@ -0,0 +1,13 @@ +# Declarative fuzz module registry. +# Add a family: drop fuzzing/src/math/_fuzz.cpp and append MODULE below. + +set(CCMATH_FUZZ_MODULE_ORDER + basic + compare + expo + fmanip + nearest + power + trig + misc +) diff --git a/cmake/config/OracleCampaignRegistry.cmake b/cmake/config/OracleCampaignRegistry.cmake new file mode 100644 index 00000000..5c423dd2 --- /dev/null +++ b/cmake/config/OracleCampaignRegistry.cmake @@ -0,0 +1,159 @@ +# Declarative rigorous oracle registry. +# Add an executable: set TARGET and SOURCE, append ID to CCMATH_ORACLE__EXECUTABLE_IDS. +# Add ctests: append test ID to CCMATH_ORACLE___CTESTS and define CCMATH_ORACLE__TEST__*. + +set(CCMATH_ORACLE_MPFR_EXECUTABLE_IDS + mpfr_pow_double + mpfr_pow_float + mpfr_fma_double + mpfr_fmaf + mpfr_powl + powl_characterization + pow_search + cross_libm_pow +) + +set(CCMATH_ORACLE_MPFR_mpfr_pow_double_TARGET ccmath-rigorous-mpfr-pow) +set(CCMATH_ORACLE_MPFR_mpfr_pow_double_SOURCE ../shared/oracle/mpfr_pow_double.cpp) +set(CCMATH_ORACLE_MPFR_mpfr_pow_double_CTESTS mpfr_pow_double_public mpfr_pow_double_nobuiltin) + +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_public_NAME ccmath-rigorous-mpfr-pow) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_public_TARGET ccmath-rigorous-mpfr-pow) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_public_REQUIRES "NOT CCMATH_TEST_DISABLE_RUNTIME_BUILTIN") +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_public_TIMEOUT 600) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_public_LABELS rigorous mpfr) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_public_ARGS --mode=quick --path=public_default) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_public_LOG_OUTPUT mpfr-pow-double-public_default.json) + +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_nobuiltin_NAME ccmath-rigorous-mpfr-pow-nobuiltin) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_nobuiltin_TARGET ccmath-rigorous-mpfr-pow) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_nobuiltin_REQUIRES CCMATH_TEST_DISABLE_RUNTIME_BUILTIN) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_nobuiltin_TIMEOUT 600) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_nobuiltin_LABELS rigorous mpfr) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_nobuiltin_ARGS --mode=quick --path=runtime_no_builtin,generic_modeled_domain) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_double_nobuiltin_LOG_OUTPUT mpfr-pow-double-nobuiltin.json) + +set(CCMATH_ORACLE_MPFR_mpfr_pow_float_TARGET ccmath-rigorous-mpfr-powf) +set(CCMATH_ORACLE_MPFR_mpfr_pow_float_SOURCE ../shared/oracle/mpfr_pow_float.cpp) +set(CCMATH_ORACLE_MPFR_mpfr_pow_float_CTESTS mpfr_pow_float_public mpfr_pow_float_reduced mpfr_pow_float_nobuiltin) + +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_public_NAME ccmath-rigorous-mpfr-powf) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_public_TARGET ccmath-rigorous-mpfr-powf) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_public_TIMEOUT 900) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_public_LABELS rigorous mpfr) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_public_ARGS --mode=quick --path=public_default) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_public_LOG_OUTPUT mpfr-powf-public_default.json) + +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_reduced_NAME ccmath-rigorous-mpfr-powf-reduced) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_reduced_TARGET ccmath-rigorous-mpfr-powf) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_reduced_TIMEOUT 900) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_reduced_LABELS rigorous mpfr) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_reduced_ARGS --mode=quick --path=public_default --domain=mantissa-sweep,subnormal-x) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_reduced_LOG_OUTPUT mpfr-powf-reduced-domains.json) + +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_nobuiltin_NAME ccmath-rigorous-mpfr-powf-nobuiltin) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_nobuiltin_TARGET ccmath-rigorous-mpfr-powf) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_nobuiltin_REQUIRES CCMATH_TEST_DISABLE_RUNTIME_BUILTIN) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_nobuiltin_TIMEOUT 900) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_nobuiltin_LABELS rigorous mpfr) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_nobuiltin_ARGS --mode=quick --path=runtime_no_builtin,generic_modeled_domain) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_pow_float_nobuiltin_LOG_OUTPUT mpfr-powf-nobuiltin.json) + +set(CCMATH_ORACLE_MPFR_mpfr_fma_double_TARGET ccmath-rigorous-mpfr-fma) +set(CCMATH_ORACLE_MPFR_mpfr_fma_double_SOURCE ../shared/oracle/mpfr_fma_double.cpp) +set(CCMATH_ORACLE_MPFR_mpfr_fma_double_CTESTS mpfr_fma_double) + +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fma_double_NAME ccmath-rigorous-mpfr-fma) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fma_double_TARGET ccmath-rigorous-mpfr-fma) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fma_double_TIMEOUT 600) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fma_double_LABELS rigorous mpfr) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fma_double_ARGS --mode=quick --path=public_default,software_fallback,quiet_generic --rounding-modes=all) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fma_double_LOG_OUTPUT mpfr-fma-double.json) + +set(CCMATH_ORACLE_MPFR_mpfr_fmaf_TARGET ccmath-rigorous-mpfr-fmaf) +set(CCMATH_ORACLE_MPFR_mpfr_fmaf_SOURCE ../shared/oracle/mpfr_fma_float.cpp) +set(CCMATH_ORACLE_MPFR_mpfr_fmaf_CTESTS mpfr_fmaf) + +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fmaf_NAME ccmath-rigorous-mpfr-fmaf) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fmaf_TARGET ccmath-rigorous-mpfr-fmaf) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fmaf_TIMEOUT 600) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fmaf_LABELS rigorous mpfr) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fmaf_ARGS --mode=quick --path=public_default,software_fallback,quiet_generic --rounding-modes=all) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_fmaf_LOG_OUTPUT mpfr-fmaf.json) + +set(CCMATH_ORACLE_MPFR_mpfr_powl_TARGET ccmath-rigorous-mpfr-powl) +set(CCMATH_ORACLE_MPFR_mpfr_powl_SOURCE ../shared/oracle/mpfr_powl.cpp) +set(CCMATH_ORACLE_MPFR_mpfr_powl_CTESTS mpfr_powl) + +set(CCMATH_ORACLE_MPFR_TEST_mpfr_powl_NAME ccmath-rigorous-mpfr-powl) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_powl_TARGET ccmath-rigorous-mpfr-powl) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_powl_TIMEOUT 300) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_powl_LABELS rigorous mpfr) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_powl_ARGS --mode=quick) +set(CCMATH_ORACLE_MPFR_TEST_mpfr_powl_JSON_OUTPUT mpfr-powl.json) + +set(CCMATH_ORACLE_MPFR_powl_characterization_TARGET ccmath-rigorous-powl-characterization) +set(CCMATH_ORACLE_MPFR_powl_characterization_SOURCE ../shared/oracle/powl_platform_characterization.cpp) +set(CCMATH_ORACLE_MPFR_powl_characterization_CTESTS powl_characterization) + +set(CCMATH_ORACLE_MPFR_TEST_powl_characterization_NAME ccmath-rigorous-powl-characterization) +set(CCMATH_ORACLE_MPFR_TEST_powl_characterization_TARGET ccmath-rigorous-powl-characterization) +set(CCMATH_ORACLE_MPFR_TEST_powl_characterization_TIMEOUT 120) +set(CCMATH_ORACLE_MPFR_TEST_powl_characterization_LABELS rigorous mpfr) +set(CCMATH_ORACLE_MPFR_TEST_powl_characterization_JSON_OUTPUT powl-platform.json) + +set(CCMATH_ORACLE_MPFR_pow_search_TARGET ccmath-rigorous-pow-search) +set(CCMATH_ORACLE_MPFR_pow_search_SOURCE ../shared/oracle/pow_adversarial_search.cpp) +set(CCMATH_ORACLE_MPFR_pow_search_CTESTS pow_search_smoke) + +set(CCMATH_ORACLE_MPFR_TEST_pow_search_smoke_NAME ccmath-rigorous-pow-search-smoke) +set(CCMATH_ORACLE_MPFR_TEST_pow_search_smoke_TARGET ccmath-rigorous-pow-search) +set(CCMATH_ORACLE_MPFR_TEST_pow_search_smoke_REQUIRES CCMATH_ENABLE_POW_SEARCH_TOOLS) +set(CCMATH_ORACLE_MPFR_TEST_pow_search_smoke_TIMEOUT 60) +set(CCMATH_ORACLE_MPFR_TEST_pow_search_smoke_LABELS rigorous mpfr) +set(CCMATH_ORACLE_MPFR_TEST_pow_search_smoke_ARGS --mode=near-one --count=256 --path=public_default) + +set(CCMATH_ORACLE_MPFR_cross_libm_pow_TARGET ccmath-rigorous-cross-libm-pow) +set(CCMATH_ORACLE_MPFR_cross_libm_pow_SOURCE ../shared/oracle/cross_libm_pow.cpp) +set(CCMATH_ORACLE_MPFR_cross_libm_pow_CTESTS cross_libm_pow_smoke) + +set(CCMATH_ORACLE_MPFR_TEST_cross_libm_pow_smoke_NAME ccmath-rigorous-cross-libm-pow-smoke) +set(CCMATH_ORACLE_MPFR_TEST_cross_libm_pow_smoke_TARGET ccmath-rigorous-cross-libm-pow) +set(CCMATH_ORACLE_MPFR_TEST_cross_libm_pow_smoke_REQUIRES CCMATH_ENABLE_CROSS_LIBM_TESTS) +set(CCMATH_ORACLE_MPFR_TEST_cross_libm_pow_smoke_TIMEOUT 60) +set(CCMATH_ORACLE_MPFR_TEST_cross_libm_pow_smoke_LABELS rigorous mpfr) +set(CCMATH_ORACLE_MPFR_TEST_cross_libm_pow_smoke_ARGS --corpus=quick --format=json --output=cross-libm-pow-quick.json) + +set(CCMATH_ORACLE_COREMATH_EXECUTABLE_IDS + coremath_pow_double + coremath_pow_float +) + +set(CCMATH_ORACLE_COREMATH_coremath_pow_double_TARGET ccmath-rigorous-coremath-pow) +set(CCMATH_ORACLE_COREMATH_coremath_pow_double_SOURCE ../shared/oracle/coremath_pow_double.cpp) +set(CCMATH_ORACLE_COREMATH_coremath_pow_double_CTESTS coremath_pow_double) + +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_double_NAME ccmath-rigorous-coremath-pow) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_double_TARGET ccmath-rigorous-coremath-pow) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_double_TIMEOUT 900) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_double_LABELS rigorous coremath) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_double_ARGS --mode=quick --path=generic_runtime --rounding-modes=all) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_double_LOG_OUTPUT coremath-pow-double-generic_runtime.json) + +set(CCMATH_ORACLE_COREMATH_coremath_pow_float_TARGET ccmath-rigorous-coremath-powf) +set(CCMATH_ORACLE_COREMATH_coremath_pow_float_SOURCE ../shared/oracle/coremath_pow_float.cpp) +set(CCMATH_ORACLE_COREMATH_coremath_pow_float_CTESTS coremath_pow_float coremath_pow_float_reduced) + +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_NAME ccmath-rigorous-coremath-powf) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_TARGET ccmath-rigorous-coremath-powf) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_TIMEOUT 1200) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_LABELS rigorous coremath) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_ARGS --mode=quick --path=generic_runtime --rounding-modes=all) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_LOG_OUTPUT coremath-powf-generic_runtime.json) + +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_reduced_NAME ccmath-rigorous-coremath-powf-reduced) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_reduced_TARGET ccmath-rigorous-coremath-powf) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_reduced_TIMEOUT 1200) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_reduced_LABELS rigorous coremath) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_reduced_ARGS --mode=quick --path=generic_runtime --rounding-modes=all --domain=mantissa-sweep,subnormal-x) +set(CCMATH_ORACLE_COREMATH_TEST_coremath_pow_float_reduced_LOG_OUTPUT coremath-powf-reduced-domains.json) diff --git a/cmake/config/ProjectCompileDefinitions.cmake b/cmake/config/ProjectCompileDefinitions.cmake index ee958cfc..17872d03 100644 --- a/cmake/config/ProjectCompileDefinitions.cmake +++ b/cmake/config/ProjectCompileDefinitions.cmake @@ -1,7 +1,13 @@ -if (CCMATH_ENABLE_RUNTIME_SIMD) - target_compile_definitions(ccmath INTERFACE CCM_CONFIG_USE_RT_SIMD) -endif () +include(cmake/config/BuildManifest.cmake) +ccmath_manifest_apply_library_compile_definitions(ccmath) -if (CCMATH_DISABLE_ERRNO) - target_compile_definitions(ccmath INTERFACE CCM_CONFIG_DISABLE_ERRNO) -endif () \ No newline at end of file +# ccmath is header-only, so the math compiles in the consumer's translation unit. +# Deterministic mode must stop the compiler from contracting stray a*b+c into a +# hardware FMA (which would diverge between FMA and non-FMA targets); the flag has +# to be INTERFACE so it reaches consumers. MSVC does not contract under the default +# /fp:precise, so no MSVC flag is needed. +if (CCMATH_ENABLE_DETERMINISTIC) + target_compile_options(ccmath INTERFACE + $<$>:-ffp-contract=off> + ) +endif () diff --git a/cmake/config/ShowcaseDeps.cmake b/cmake/config/ShowcaseDeps.cmake new file mode 100644 index 00000000..23f88cc6 --- /dev/null +++ b/cmake/config/ShowcaseDeps.cmake @@ -0,0 +1,73 @@ +include_guard(GLOBAL) + +include(FetchContent) + +function(ccmath_showcase_fetch_gcem) + if (NOT CCMATH_SHOWCASE_ENABLE_GCEM) + return() + endif () + if (TARGET showcase::gcem) + return() + endif () + + set(_CCMATH_GCEM_VERSION v1.18.0) + FetchContent_Declare( + gcem + GIT_REPOSITORY https://github.com/kthohr/gcem.git + GIT_TAG ${_CCMATH_GCEM_VERSION} + GIT_SHALLOW TRUE + ) + FetchContent_GetProperties(gcem) + if (NOT gcem_POPULATED) + FetchContent_Populate(gcem) + endif () + + add_library(showcase_gcem INTERFACE) + add_library(showcase::gcem ALIAS showcase_gcem) + target_include_directories(showcase_gcem SYSTEM INTERFACE ${gcem_SOURCE_DIR}/include) +endfunction() + +function(ccmath_showcase_fetch_gtest) + if (TARGET gtest::gtest) + return() + endif () + + set(_CCMATH_GTEST_VERSION v1.15.2) + if (WIN32) + set(gtest_force_shared_crt ON) + set(gtest_disable_pthreads ON) + endif () + set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) + set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) + set(gtest_build_samples OFF CACHE BOOL "" FORCE) + set(gtest_build_tests OFF CACHE BOOL "" FORCE) + + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG ${_CCMATH_GTEST_VERSION} + GIT_SHALLOW TRUE + ) + FetchContent_MakeAvailable(googletest) + + if (NOT TARGET gtest::gtest) + add_library(gtest::gtest ALIAS gtest) + endif () +endfunction() + +function(ccmath_showcase_configure_target TARGET) + if (NOT TARGET ${TARGET}) + message(FATAL_ERROR "ccmath_showcase_configure_target: ${TARGET} does not exist") + endif () + target_compile_features(${TARGET} PRIVATE cxx_std_20) + target_include_directories(${TARGET} PRIVATE + ${CMAKE_SOURCE_DIR}/showcase/common + ${CMAKE_SOURCE_DIR}/benchmarks + ${CMAKE_SOURCE_DIR}/tests + ${CMAKE_SOURCE_DIR}/tests/shared/utils + ) + if (CCMATH_SHOWCASE_ENABLE_GCEM) + target_link_libraries(${TARGET} PRIVATE showcase::gcem) + target_compile_definitions(${TARGET} PRIVATE CCMATH_SHOWCASE_HAS_GCEM=1) + endif () +endfunction() diff --git a/cmake/config/TestCtestTargets.cmake b/cmake/config/TestCtestTargets.cmake new file mode 100644 index 00000000..deca0765 --- /dev/null +++ b/cmake/config/TestCtestTargets.cmake @@ -0,0 +1,57 @@ +# Convenience targets that run labeled ctest suites from the build tree root. + +include_guard(GLOBAL) + +if (NOT CCMATH_BUILD_TESTS) + return () +endif () + +include(ProcessorCount) +ProcessorCount(_ccmath_ctest_job_count) +if (_ccmath_ctest_job_count EQUAL 0) + set(_ccmath_ctest_job_count 1) +endif () + +function(_ccmath_add_ctest_label_target TARGET_NAME TEST_LABEL) + set(_ctest_args --output-on-failure -j ${_ccmath_ctest_job_count}) + + if (CMAKE_CONFIGURATION_TYPES) + list(APPEND _ctest_args -C $) + endif () + + if (TEST_LABEL) + list(APPEND _ctest_args -L ${TEST_LABEL}) + endif () + + if (TEST_LABEL) + set(_ctest_comment "Running ctest label ${TEST_LABEL}") + else () + set(_ctest_comment "Running all ctest tests") + endif () + + add_custom_target(${TARGET_NAME} + COMMAND ${CMAKE_CTEST_COMMAND} ${_ctest_args} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + USES_TERMINAL + COMMENT ${_ctest_comment} + ) +endfunction() + +if (CCMATH_BUILD_SIMPLE_TESTS) + _ccmath_add_ctest_label_target(ccmath-ctest-simple simple) +endif () + +if (CCMATH_BUILD_RIGOROUS_TESTS) + _ccmath_add_ctest_label_target(ccmath-ctest-rigorous rigorous) + _ccmath_add_ctest_label_target(ccmath-ctest-aspirational aspirational) + + if (CCMATH_ENABLE_MPFR_TESTS) + _ccmath_add_ctest_label_target(ccmath-ctest-rigorous-mpfr mpfr) + endif () + + if (CCMATH_ENABLE_COREMATH_TESTS) + _ccmath_add_ctest_label_target(ccmath-ctest-rigorous-coremath coremath) + endif () +endif () + +_ccmath_add_ctest_label_target(ccmath-ctest-all "") diff --git a/cmake/config/TestModuleRegistry.cmake b/cmake/config/TestModuleRegistry.cmake new file mode 100644 index 00000000..99b4abb7 --- /dev/null +++ b/cmake/config/TestModuleRegistry.cmake @@ -0,0 +1,26 @@ +# Declarative unit-test module registry. +# Add a new public math family: drop *_test.cpp under tests/src/math// and +# append MODULE TARGET pair below (LLVM-libc test/src layout). + +set(CCMATH_UNIT_MODULE_basic ccmath-simple-basic) +set(CCMATH_UNIT_MODULE_basic_EXCLUDE + src/math/basic/fma_constexpr_upward_config_test.cpp) +set(CCMATH_UNIT_MODULE_compare ccmath-simple-compare) +set(CCMATH_UNIT_MODULE_expo ccmath-simple-exponential) +set(CCMATH_UNIT_MODULE_fmanip ccmath-simple-fmanip) +set(CCMATH_UNIT_MODULE_nearest ccmath-simple-nearest) +set(CCMATH_UNIT_MODULE_power ccmath-simple-power) +set(CCMATH_UNIT_MODULE_trig ccmath-simple-trigonometric) +set(CCMATH_UNIT_MODULE_ext ccmath-simple-ext) +set(CCMATH_UNIT_MODULE_ext_EXCLUDE + src/ext/constexpr_smoke_test.cpp) + +set(CCMATH_UNIT_MATH_MODULE_ORDER + basic + compare + expo + fmanip + nearest + power + trig +) diff --git a/cmake/config/TestOracleTargets.cmake b/cmake/config/TestOracleTargets.cmake new file mode 100644 index 00000000..cd1b1063 --- /dev/null +++ b/cmake/config/TestOracleTargets.cmake @@ -0,0 +1,134 @@ +# Helper functions for rigorous MPFR and CORE-MATH oracle executables. + +include_guard(GLOBAL) + +function(ccmath_add_rigorous_oracle_target TARGET) + set(options) + set(oneValueArgs BACKEND) + set(multiValueArgs SOURCES LIBRARIES INCLUDE_DIRECTORIES) + cmake_parse_arguments(CCM_ORACLE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT CCM_ORACLE_BACKEND) + message(FATAL_ERROR "ccmath_add_rigorous_oracle_target requires BACKEND MPFR or COREMATH") + endif () + if (NOT CCM_ORACLE_SOURCES) + message(FATAL_ERROR "ccmath_add_rigorous_oracle_target requires at least one source file") + endif () + + add_executable(${TARGET}) + target_sources(${TARGET} PRIVATE ${CCM_ORACLE_SOURCES}) + target_link_libraries(${TARGET} PRIVATE ccmath::ccmath ccmath::ext) + + if (CCM_ORACLE_BACKEND STREQUAL "MPFR") + if (NOT TARGET ccmath::mpfr_oracle) + message(FATAL_ERROR "ccmath_add_rigorous_oracle_target(${TARGET}): ccmath::mpfr_oracle target is unavailable") + endif () + target_link_libraries(${TARGET} PRIVATE ccmath::mpfr_oracle) + elseif (CCM_ORACLE_BACKEND STREQUAL "COREMATH") + if (NOT TARGET ccmath::coremath_oracle) + message(FATAL_ERROR "ccmath_add_rigorous_oracle_target(${TARGET}): ccmath::coremath_oracle target is unavailable") + endif () + target_link_libraries(${TARGET} PRIVATE ccmath::coremath_oracle) + if (NOT WIN32) + target_link_libraries(${TARGET} PRIVATE m) + endif () + else () + message(FATAL_ERROR "ccmath_add_rigorous_oracle_target(${TARGET}): unknown BACKEND ${CCM_ORACLE_BACKEND}") + endif () + + if (CCM_ORACLE_LIBRARIES) + target_link_libraries(${TARGET} PRIVATE ${CCM_ORACLE_LIBRARIES}) + endif () + + target_include_directories(${TARGET} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../shared + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CCM_ORACLE_INCLUDE_DIRECTORIES} + ) + target_compile_features(${TARGET} PRIVATE cxx_std_17) + ccmath_apply_pow_validation_options(${TARGET}) +endfunction() + +function(ccmath_add_rigorous_oracle_test TEST_NAME TARGET) + set(options) + set(oneValueArgs TIMEOUT WORKING_DIRECTORY) + set(multiValueArgs LABELS ARGS) + cmake_parse_arguments(CCM_ORACLE_TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT TARGET ${TARGET}) + message(FATAL_ERROR "ccmath_add_rigorous_oracle_test(${TEST_NAME}): target ${TARGET} does not exist") + endif () + + set(_command ${TARGET}) + if (CCM_ORACLE_TEST_ARGS) + list(APPEND _command ${CCM_ORACLE_TEST_ARGS}) + endif () + + add_test(NAME ${TEST_NAME} COMMAND ${_command}) + + if (CCM_ORACLE_TEST_LABELS) + set_tests_properties(${TEST_NAME} PROPERTIES LABELS "${CCM_ORACLE_TEST_LABELS}") + endif () + if (CCM_ORACLE_TEST_TIMEOUT) + set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT ${CCM_ORACLE_TEST_TIMEOUT}) + endif () + if (CCM_ORACLE_TEST_WORKING_DIRECTORY) + set_tests_properties(${TEST_NAME} PROPERTIES WORKING_DIRECTORY "${CCM_ORACLE_TEST_WORKING_DIRECTORY}") + endif () +endfunction() + +function(ccmath_ensure_mpfr_oracle_target TARGET) + set(options) + set(oneValueArgs) + set(multiValueArgs SOURCES) + cmake_parse_arguments(CCM_ENSURE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (TARGET ${TARGET}) + return() + endif () + if (NOT CCM_ENSURE_SOURCES) + message(FATAL_ERROR "ccmath_ensure_mpfr_oracle_target(${TARGET}): SOURCES is required") + endif () + ccmath_add_rigorous_oracle_target(${TARGET} BACKEND MPFR SOURCES ${CCM_ENSURE_SOURCES}) +endfunction() + +function(ccmath_ensure_coremath_oracle_target TARGET) + set(options) + set(oneValueArgs) + set(multiValueArgs SOURCES) + cmake_parse_arguments(CCM_ENSURE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (TARGET ${TARGET}) + return() + endif () + if (NOT CCM_ENSURE_SOURCES) + message(FATAL_ERROR "ccmath_ensure_coremath_oracle_target(${TARGET}): SOURCES is required") + endif () + ccmath_add_rigorous_oracle_target(${TARGET} BACKEND COREMATH SOURCES ${CCM_ENSURE_SOURCES}) +endfunction() + +function(ccmath_add_oracle_ctest TEST_NAME) + set(options) + set(oneValueArgs TARGET TIMEOUT WORKING_DIRECTORY LOG_OUTPUT JSON_OUTPUT) + set(multiValueArgs LABELS ARGS) + cmake_parse_arguments(CCM_CTEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT CCM_CTEST_TARGET) + message(FATAL_ERROR "ccmath_add_oracle_ctest(${TEST_NAME}): TARGET is required") + endif () + + set(_command_args ${CCM_CTEST_ARGS}) + if (CCM_CTEST_LOG_OUTPUT) + list(APPEND _command_args --log-output=${CCM_CTEST_LOG_OUTPUT}) + endif () + if (CCM_CTEST_JSON_OUTPUT) + list(APPEND _command_args --json-output=${CCM_CTEST_JSON_OUTPUT}) + endif () + + ccmath_add_rigorous_oracle_test(${TEST_NAME} ${CCM_CTEST_TARGET} + LABELS ${CCM_CTEST_LABELS} + TIMEOUT ${CCM_CTEST_TIMEOUT} + ARGS ${_command_args} + WORKING_DIRECTORY ${CCM_CTEST_WORKING_DIRECTORY}) +endfunction() diff --git a/cmake/config/TestValidationOptions.cmake b/cmake/config/TestValidationOptions.cmake new file mode 100644 index 00000000..51bab85d --- /dev/null +++ b/cmake/config/TestValidationOptions.cmake @@ -0,0 +1,55 @@ +# Apply pow validation compile options to test and oracle targets. + +function(ccmath_get_pow_validation_configuration_name OUT_VAR) + set(_parts "") + if (CCMATH_TEST_DISABLE_RUNTIME_BUILTIN) + list(APPEND _parts "no-builtin") + endif () + if (CCMATH_TEST_DISABLE_FMA) + list(APPEND _parts "no-fma") + elseif (CCMATH_TEST_FORCE_FMA) + list(APPEND _parts "force-fma") + endif () + if (CCMATH_DISABLE_RUNTIME_SIMD) + list(APPEND _parts "no-simd") + endif () + if (CCMATH_DISABLE_REDUCED_PRECISION_POWL) + list(APPEND _parts "powl-strict") + else () + list(APPEND _parts "powl-fallback") + endif () + if (NOT _parts) + set(_parts "default") + endif () + string(REPLACE ";" "-" _name "${_parts}") + set(${OUT_VAR} "${_name}" PARENT_SCOPE) +endfunction() + +function(ccmath_apply_pow_validation_options TARGET) + if (NOT TARGET ${TARGET}) + message(FATAL_ERROR "ccmath_apply_pow_validation_options: target ${TARGET} does not exist") + endif () + + if (CCMATH_TEST_DISABLE_RUNTIME_BUILTIN) + target_compile_definitions(${TARGET} PRIVATE CCM_CONFIG_TEST_DISABLE_RUNTIME_BUILTIN_POW) + endif () + + if (CCMATH_TEST_DISABLE_FMA) + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang|IntelLLVM") + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|i[3-6]86)$") + target_compile_options(${TARGET} PRIVATE -fno-fma) + endif () + endif () + endif () + + if (CCMATH_TEST_FORCE_FMA) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|i[3-6]86)$") + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang|IntelLLVM") + target_compile_options(${TARGET} PRIVATE -mfma) + endif () + endif () + endif () + + ccmath_get_pow_validation_configuration_name(_config_name) + target_compile_definitions(${TARGET} PRIVATE CCM_CONFIG_TEST_POW_VALIDATION_NAME="${_config_name}") +endfunction() diff --git a/cmake/config/UserOptions.cmake b/cmake/config/UserOptions.cmake index 625f1639..54d5532f 100644 --- a/cmake/config/UserOptions.cmake +++ b/cmake/config/UserOptions.cmake @@ -12,10 +12,66 @@ option(CCMATH_BUILD_EXAMPLES "Build ccmath examples" ${CCMATH_PROJECT_IS_TOP_LEV # Enable building of ccmath tests. If OFF, tests are skipped. Should only be used by ccmath developers. option(CCMATH_BUILD_TESTS "Build ccmath tests" ${CCMATH_PROJECT_IS_TOP_LEVEL}) +# CCMATH_BUILD_SIMPLE_TESTS: +# Build the lightweight unit/regression suite intended for every developer and CI run. +option(CCMATH_BUILD_SIMPLE_TESTS "Build the lightweight ccmath test project" ${CCMATH_PROJECT_IS_TOP_LEVEL}) + +# CCMATH_BUILD_RIGOROUS_TESTS: +# Build the heavier validation/oracle suite intended for post-merge, nightly, or release validation. +option(CCMATH_BUILD_RIGOROUS_TESTS "Build the rigorous ccmath validation project" OFF) + +# CCMATH_ENABLE_MPFR_TESTS: +# Enable MPFR-backed oracle validation binaries when MPFR and GMP are available. +option(CCMATH_ENABLE_MPFR_TESTS "Enable MPFR-backed rigorous oracle executables" OFF) + +# CCMATH_ENABLE_COREMATH_TESTS: +# Enable CORE-MATH-backed oracle validation binaries when CORE-MATH is discoverable. +option(CCMATH_ENABLE_COREMATH_TESTS "Enable CORE-MATH-backed rigorous oracle executables" OFF) + +# CCMATH_COREMATH_ROOT: +# Optional install prefix for a pre-built CORE-MATH oracle library (include + lib). +set(CCMATH_COREMATH_ROOT "" CACHE PATH "Install prefix for CORE-MATH oracle library") + +# CCMATH_FETCH_COREMATH: +# Last-resort FetchContent build when prefix/pkg-config discovery fails. +option(CCMATH_FETCH_COREMATH "Fetch and build CORE-MATH sources when not found on the system" OFF) + +# CCMATH_ENABLE_POW_SEARCH_TOOLS: +# Build standalone pow adversarial-search tools. +option(CCMATH_ENABLE_POW_SEARCH_TOOLS "Build standalone pow adversarial-search tools" OFF) + +# CCMATH_ENABLE_CROSS_LIBM_TESTS: +# Build optional cross-libm differential pow validation tools. +option(CCMATH_ENABLE_CROSS_LIBM_TESTS "Build optional cross-libm pow differential tools" OFF) + +# CCMATH_TEST_DISABLE_RUNTIME_BUILTIN: +# Disable runtime __builtin_pow dispatch in pow_rt for validation builds. +option(CCMATH_TEST_DISABLE_RUNTIME_BUILTIN "Disable runtime builtin pow in validation builds" OFF) + +# CCMATH_TEST_DISABLE_FMA: +# Force non-FMA generic pow kernel branches in validation builds. +option(CCMATH_TEST_DISABLE_FMA "Disable FMA pow kernel branches in validation builds" OFF) + +# CCMATH_TEST_FORCE_FMA: +# Request FMA-capable compilation for validation builds when supported. +option(CCMATH_TEST_FORCE_FMA "Force FMA-capable compilation for validation builds" OFF) + # CCMATH_BUILD_BENCHMARKS: # Enable building of ccmath benchmarks. If OFF, benchmarks are skipped. option(CCMATH_BUILD_BENCHMARKS "Build ccmath benchmarks" OFF) +# CCMATH_BUILD_SHOWCASE: +# Build the external library comparison showcase (GCEM first, MPFR accuracy, gbench perf). +option(CCMATH_BUILD_SHOWCASE "Build ccmath showcase comparisons" OFF) + +# CCMATH_SHOWCASE_ENABLE_GCEM: +# Fetch and enable the GCEM backend in showcase targets. +option(CCMATH_SHOWCASE_ENABLE_GCEM "Enable GCEM in showcase comparisons" ${CCMATH_PROJECT_IS_TOP_LEVEL}) + +# CCMATH_SHOWCASE_ENABLE_MPFR: +# Build MPFR-backed showcase accuracy executables when MPFR is available. +option(CCMATH_SHOWCASE_ENABLE_MPFR "Enable MPFR accuracy in showcase comparisons" ${CCMATH_PROJECT_IS_TOP_LEVEL}) + # CCMATH_BUILD_FUZZING: # Build libFuzzer targets (Clang only). Couples fuzzer with ASan and UBSan. option(CCMATH_BUILD_FUZZING "Build libFuzzer fuzz targets (Clang only)" OFF) @@ -34,17 +90,8 @@ option(CCMATH_ENABLE_EXTENSIONS "Enable the extended ccmath library that adds additional methods beyond the standard" ${CCMATH_PROJECT_IS_TOP_LEVEL}) -# CCMATH_DISABLE_ERRNO: -# Disable usage of errno during runtime breaking standard compliance, but may lead to faster evaluation and smaller binaries. -option(CCMATH_DISABLE_ERRNO - "Disable the use of errno in ccmath during runtime (may lead to faster evaluations but is non-standard)" - OFF) - -# CCMATH_ENABLE_RUNTIME_SIMD: -# Enable runtime SIMD optimizations for faster evaluations at runtime. -option(CCMATH_DISABLE_RUNTIME_SIMD - "Enable SIMD optimization for runtime evaluation (does not affect compile-time)" - OFF) +include(cmake/config/BuildManifest.cmake) +ccmath_manifest_declare_library_options() # CCMATH_DISABLE_SVML_USAGE: # Disable the use of SVML (Short Vector Math Library) if supported by the compiler. @@ -52,6 +99,16 @@ option(CCMATH_DISABLE_SVML_USAGE "Disable the use of SVML functions in ccmath (if supported by compiler)" OFF) +# CCMATH_SIMD_PREFER: +# Opt-in: choose which SIMD instruction set ccmath builds compile for. +# DEFAULT - use whatever the toolchain / -march already selects (no extra flag). +# LOWEST - the most portable baseline for the architecture (SSE2 on x86_64, NEON on AArch64). +# HIGHEST - the best instruction set in ccmath's supported ladder the compiler can emit (up to AVX2; AVX-512 excluded). +# Defaults to DEFAULT (does nothing); downstream consumers may enable or disable it. +set(CCMATH_SIMD_PREFER "DEFAULT" CACHE STRING + "Preferred SIMD instruction set for ccmath builds: DEFAULT, LOWEST or HIGHEST") +set_property(CACHE CCMATH_SIMD_PREFER PROPERTY STRINGS DEFAULT LOWEST HIGHEST) + # CCMATH_DISABLE_CMAKE_FEATURE_CHECKS: # Disable cmakes ability to check for certain features at the CMake level. option(CCMATH_DISABLE_CMAKE_FEATURE_CHECKS diff --git a/cmake/coremath/CMakeLists.txt b/cmake/coremath/CMakeLists.txt new file mode 100644 index 00000000..6d4ce618 --- /dev/null +++ b/cmake/coremath/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.18) +project(ccmath_coremath_oracle LANGUAGES C) + +if (NOT CORE_MATH_SOURCE_DIR) + message(FATAL_ERROR "CORE_MATH_SOURCE_DIR must point at a CORE-MATH source tree") +endif () + +add_library(coremath_oracle STATIC + ${CORE_MATH_SOURCE_DIR}/src/binary64/pow/pow.c + ${CORE_MATH_SOURCE_DIR}/src/binary32/pow/powf.c +) + +target_include_directories(coremath_oracle + PUBLIC + $ + $ + PRIVATE + ${CORE_MATH_SOURCE_DIR}/src/binary64/pow +) + +if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$") + target_compile_options(coremath_oracle PRIVATE -march=x86-64-v3) +endif () + +if (MSVC) + target_compile_options(coremath_oracle PRIVATE -O2 -fp:strict) +else () + target_compile_options(coremath_oracle PRIVATE -O2 -fno-fast-math -ffp-contract=off) +endif () + +include(GNUInstallDirs) + +install(TARGETS coremath_oracle + EXPORT ccmath_coremath_oracle_targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/crmath.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +install(EXPORT ccmath_coremath_oracle_targets + FILE ccmath_coremath_oracle_targets.cmake + NAMESPACE ccmath:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ccmath_coremath_oracle +) diff --git a/cmake/coremath/crmath.h b/cmake/coremath/crmath.h new file mode 100644 index 00000000..21ef5498 --- /dev/null +++ b/cmake/coremath/crmath.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + + float cr_powf(float x, float y); + double cr_pow(double x, double y); + +#ifdef __cplusplus +} +#endif diff --git a/cmake/features/CheckAllSupportedSimdFeatures.cmake b/cmake/features/CheckAllSupportedSimdFeatures.cmake index dc810ac3..a814ab4e 100644 --- a/cmake/features/CheckAllSupportedSimdFeatures.cmake +++ b/cmake/features/CheckAllSupportedSimdFeatures.cmake @@ -2,6 +2,10 @@ if (NOT DEFINED CCMATH_ROOT_DIR) message(FATAL_ERROR "CCMATH_ROOT_DIR is not defined. Did you forget to include the main CMakeLists.txt?") endif () +# Select the preferred instruction set first so the FMA / SVML probes below are +# evaluated at the chosen level (via CMAKE_REQUIRED_FLAGS). +include(${CCMATH_ROOT_DIR}/cmake/features/simd/SelectSimdInstructionSet.cmake) + include(${CCMATH_ROOT_DIR}/cmake/features/simd/CheckFMASupport.cmake) if (NOT CCMATH_DISABLE_SVML_USAGE) diff --git a/cmake/features/simd/SelectSimdInstructionSet.cmake b/cmake/features/simd/SelectSimdInstructionSet.cmake new file mode 100644 index 00000000..d6ba1860 --- /dev/null +++ b/cmake/features/simd/SelectSimdInstructionSet.cmake @@ -0,0 +1,133 @@ +# Select and apply a preferred SIMD instruction set for ccmath's in-tree build. +# +# Controlled by CCMATH_SIMD_PREFER (DEFAULT | LOWEST | HIGHEST), declared in +# cmake/config/UserOptions.cmake: +# DEFAULT leave whatever the toolchain / -march already selects (no extra flag). +# LOWEST target the most portable baseline for the architecture +# (SSE2 on x86_64, NEON on AArch64). +# HIGHEST target the best instruction set in ccmath's supported ladder that the +# compiler can emit (up to AVX2). AVX-512 is intentionally excluded. +# +# This is opt-in: the default does nothing. When enabled, the chosen flag is +# attached to the ccmath interface target behind a BUILD_INTERFACE generator +# expression, so it affects this build tree (library, tests, examples, +# benchmarks and in-tree consumers that opt in) without leaking into the +# installed / exported package. CMAKE_REQUIRED_FLAGS is also updated so the later +# FMA / SVML feature probes are evaluated at the selected level. + +if (NOT DEFINED CCMATH_SIMD_PREFER OR CCMATH_SIMD_PREFER STREQUAL "DEFAULT") + return() +endif () + +if (NOT CCMATH_SIMD_PREFER STREQUAL "LOWEST" AND NOT CCMATH_SIMD_PREFER STREQUAL "HIGHEST") + message(WARNING "CCMath: unknown CCMATH_SIMD_PREFER='${CCMATH_SIMD_PREFER}' (expected DEFAULT, LOWEST or HIGHEST). Ignoring.") + return() +endif () + +if (NOT DEFINED CCMATH_TARGET_NAME) + set(CCMATH_TARGET_NAME ccmath) +endif () + +include(CheckCXXSourceCompiles) + +# Probe whether the compiler accepts ${_flag} and can compile ${_src}. A flag of +# NONE means "no extra flag" (used for baseline rungs; an empty string cannot be +# carried as a list element). +function(_ccmath_probe_isa _flag _src _resultvar) + set(_saved "${CMAKE_REQUIRED_FLAGS}") + if (_flag AND NOT _flag STREQUAL "NONE") + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${_flag}") + endif () + check_cxx_source_compiles("${_src}" ${_resultvar}) + set(CMAKE_REQUIRED_FLAGS "${_saved}") + set(${_resultvar} "${${_resultvar}}" PARENT_SCOPE) +endfunction() + +# Representative intrinsic per rung. +set(_src_sse2 [=[ +#include +int main() { __m128d v = _mm_set1_pd(1.0); v = _mm_add_pd(v, v); return (int) _mm_cvtsd_f64(v); } +]=]) +set(_src_sse42 [=[ +#include +int main() { __m128 v = _mm_set1_ps(1.0f); v = _mm_dp_ps(v, v, 0xFF); return (int) _mm_cvtss_f32(v); } +]=]) +set(_src_avx [=[ +#include +int main() { __m256 v = _mm256_set1_ps(1.0f); v = _mm256_add_ps(v, v); return (int) _mm256_cvtss_f32(v); } +]=]) +set(_src_avx2 [=[ +#include +int main() { __m256i v = _mm256_set1_epi32(1); v = _mm256_add_epi32(v, v); return _mm256_extract_epi32(v, 0); } +]=]) +set(_src_neon [=[ +#include +int main() { float32x4_t v = vdupq_n_f32(1.0f); v = vaddq_f32(v, v); return (int) vgetq_lane_f32(v, 0); } +]=]) + +# Build the rung ladder (low -> high) for this architecture and compiler. Only +# the rung names and flags are kept as lists; the source snippets contain +# semicolons and must never be stored as list elements (they are looked up by +# name as _src_ instead). +set(_names "") +set(_flags "") + +if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64|i[3-6]86|x86)$") + if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + # MSVC: SSE2 is the x64 baseline (no flag); no SSE4 arch switch exists. + set(_names SSE2 AVX AVX2) + set(_flags NONE "/arch:AVX" "/arch:AVX2") + else () + # Use the x86-64 microarchitecture levels (matches cmake/coremath): v1 is + # the SSE2 baseline, v2 ~ SSE4.2, v3 ~ AVX2+FMA. v4 (AVX-512) is excluded. + # A march level also caps the ISA, overriding any higher -m flag a preset + # may have set earlier on the command line. + set(_names SSE2 SSE4.2 AVX2) + set(_flags "-march=x86-64" "-march=x86-64-v2" "-march=x86-64-v3") + endif () +elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$") + # NEON is the mandatory AArch64 baseline; SVE is intentionally out of scope. + set(_names NEON) + set(_flags NONE) +else () + message(STATUS "CCMath: CCMATH_SIMD_PREFER=${CCMATH_SIMD_PREFER} has no instruction-set ladder for '${CMAKE_SYSTEM_PROCESSOR}'; leaving toolchain default.") + return() +endif () + +list(LENGTH _names _count) +math(EXPR _last "${_count} - 1") + +# LOWEST: first rung that compiles. HIGHEST: highest rung that compiles. +set(_chosen -1) +foreach (_i RANGE 0 ${_last}) + list(GET _names ${_i} _nm) + list(GET _flags ${_i} _fl) + # Look up the matching source snippet by name (e.g. SSE4.2 -> _src_sse42). + string(TOLOWER "${_nm}" _lc) + string(REPLACE "." "" _lc "${_lc}") + set(_sr "${_src_${_lc}}") + string(MAKE_C_IDENTIFIER "CCMATH_PROBE_ISA_${_nm}" _rv) + _ccmath_probe_isa("${_fl}" "${_sr}" ${_rv}) + if (${_rv}) + set(_chosen ${_i}) + if (CCMATH_SIMD_PREFER STREQUAL "LOWEST") + break() + endif () + endif () +endforeach () + +if (_chosen LESS 0) + message(WARNING "CCMath: could not select a SIMD instruction set for CCMATH_SIMD_PREFER=${CCMATH_SIMD_PREFER}; leaving toolchain default.") + return() +endif () + +list(GET _names ${_chosen} _chosen_name) +list(GET _flags ${_chosen} _chosen_flag) + +if (_chosen_flag AND NOT _chosen_flag STREQUAL "NONE") + target_compile_options(${CCMATH_TARGET_NAME} INTERFACE "$") + string(APPEND CMAKE_REQUIRED_FLAGS " ${_chosen_flag}") + message(STATUS "CCMath: SIMD instruction set (${CCMATH_SIMD_PREFER}) -> ${_chosen_name} (${_chosen_flag})") +else () + message(STATUS "CCMath: SIMD instruction set (${CCMATH_SIMD_PREFER}) -> ${_chosen_name} (toolchain baseline)") +endif () diff --git a/cmake/gen/meson.build b/cmake/gen/meson.build new file mode 100644 index 00000000..3d3ce501 --- /dev/null +++ b/cmake/gen/meson.build @@ -0,0 +1,71 @@ +# Generated by tools/gen_secondary_builds.py. Do not edit by hand. +# Exposes CCMath as a Meson dependency only. + +cpp = meson.get_compiler('cpp') + +_ccmath_builtin_bit_cast_probe = ''' +#include +int main() { + struct A { int x; }; + struct B { int y; }; + B b = __builtin_bit_cast(B, A{42}); + return b.y; +} +''' + +if not cpp.compiles(_ccmath_builtin_bit_cast_probe, name : 'ccmath builtin bit_cast support') + error('CCMath requires compiler support for __builtin_bit_cast') +endif + +_ccmath_defines = [] +if get_option('enable_runtime_simd') and not get_option('deterministic') + _ccmath_defines += '-DCCM_CONFIG_USE_RT_SIMD' +endif +if get_option('disable_errno') + _ccmath_defines += '-DCCM_CONFIG_DISABLE_ERRNO' +endif +if get_option('disable_reduced_precision_powl') + _ccmath_defines += '-DCCM_CONFIG_DISABLE_REDUCED_PRECISION_POWL' +endif +if get_option('deterministic') + _ccmath_defines += '-DCCM_CONFIG_DETERMINISTIC' +endif +if get_option('deterministic') + _ccmath_defines += '-ffp-contract=off' +endif + +_ccmath_simd_fma_probe = ''' +#include +int main() { + __m128 fma_test = _mm_fmadd_ps(_mm_set1_ps(1.0f), _mm_set1_ps(2.0f), _mm_set1_ps(3.0f)); + return static_cast(_mm_cvtss_f32(fma_test)); +} +''' + +if get_option('enable_runtime_simd') and not get_option('deterministic') and cpp.compiles(_ccmath_simd_fma_probe, name : 'ccmath runtime SIMD FMA intrinsics') + _ccmath_defines += '-DCCM_CONFIG_RT_SIMD_HAS_FMA' +endif + +_ccmath_simd_svml_probe = ''' +#include +int main() { + __m128d value = _mm_sin_pd(_mm_set1_pd(1.0)); + return static_cast(_mm_cvtsd_f64(value)); +} +''' + +if get_option('enable_runtime_simd') and not get_option('deterministic') and cpp.compiles(_ccmath_simd_svml_probe, name : 'ccmath runtime SIMD SVML intrinsics') + _ccmath_defines += '-DCCM_CONFIG_RT_SIMD_HAS_SVML' +endif + +ccmath_inc = include_directories( + '../../include', + '../../out/secondary/include', +) + +ccmath_dep = declare_dependency( + include_directories: ccmath_inc, + compile_args: _ccmath_defines, +) + +meson.override_dependency('ccmath', ccmath_dep) diff --git a/cmake/helpers/CcmBenchHelpers.cmake b/cmake/helpers/CcmBenchHelpers.cmake new file mode 100644 index 00000000..b0252b3f --- /dev/null +++ b/cmake/helpers/CcmBenchHelpers.cmake @@ -0,0 +1,86 @@ +# Google Benchmark target registration (LLVM-libc style: shared harness, per-function drivers). + +include_guard(GLOBAL) + +if (NOT DEFINED CCMATH_BENCH_ROOT) + message(FATAL_ERROR "CCMATH_BENCH_ROOT must be set before including CcmBenchHelpers.cmake") +endif () + +function(ccmath_bench_module_enabled MODULE OUT_VAR) + set(_option_var CCMATH_BENCH_MODULE_${MODULE}_OPTION) + if (NOT DEFINED ${_option_var}) + message(FATAL_ERROR "ccmath_bench_module_enabled(${MODULE}): missing ${_option_var}") + endif () + + if (CCM_BENCH_ALL) + set(${OUT_VAR} TRUE PARENT_SCOPE) + return () + endif () + + if (${${_option_var}}) + set(${OUT_VAR} TRUE PARENT_SCOPE) + else () + set(${OUT_VAR} FALSE PARENT_SCOPE) + endif () +endfunction() + +function(ccmath_add_bench_target FUNCTION) + set(options) + set(oneValueArgs MODULE) + set(multiValueArgs SOURCES COMPILE_DEFINITIONS) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (CCM_MODULE AND NOT CCM_SOURCES) + set(CCM_SOURCES "${CCMATH_BENCH_ROOT}/src/math/${CCM_MODULE}/${FUNCTION}.bench.cpp") + endif () + + if (NOT CCM_SOURCES) + message(FATAL_ERROR "ccmath_add_bench_target(${FUNCTION}): SOURCES or MODULE is required") + endif () + + set(_target ccm_benchmark_${FUNCTION}) + add_executable(${_target} ${CCM_SOURCES}) + target_link_libraries(${_target} PRIVATE ccmath::ccmath benchmark::benchmark) + target_include_directories(${_target} PRIVATE "${CCMATH_BENCH_ROOT}/shared" "${CCMATH_BENCH_ROOT}") + target_compile_features(${_target} PRIVATE cxx_std_17) + + if (CCM_COMPILE_DEFINITIONS) + target_compile_definitions(${_target} PRIVATE ${CCM_COMPILE_DEFINITIONS}) + endif () + + set(${_target}_REGISTERED TRUE PARENT_SCOPE) +endfunction() + +function(ccmath_apply_bench_registry) + if (NOT TARGET benchmark::benchmark) + message(FATAL_ERROR "ccmath_apply_bench_registry requires benchmark::benchmark") + endif () + + set(_all_targets "") + foreach (_module IN LISTS CCMATH_BENCH_MODULE_ORDER) + ccmath_bench_module_enabled(${_module} _enabled) + if (NOT _enabled) + continue () + endif () + + set(_functions_var CCMATH_BENCH_MODULE_${_module}_FUNCTIONS) + foreach (_function IN LISTS ${_functions_var}) + ccmath_add_bench_target(${_function} MODULE ${_module}) + list(APPEND _all_targets ccm_benchmark_${_function}) + endforeach () + + if (_module STREQUAL "power") + if (EXISTS "${CMAKE_SOURCE_DIR}/tools/asmlab/bench/CMakeLists.txt") + add_subdirectory("${CMAKE_SOURCE_DIR}/tools/asmlab/bench" "${CMAKE_BINARY_DIR}/asmlab_bench") + endif () + if (EXISTS "${CMAKE_SOURCE_DIR}/tools/asmlab/accuracy/CMakeLists.txt") + add_subdirectory("${CMAKE_SOURCE_DIR}/tools/asmlab/accuracy" "${CMAKE_BINARY_DIR}/asmlab_accuracy") + endif () + endif () + endforeach () + + if (_all_targets) + add_custom_target(ccm_benchmark_all) + add_dependencies(ccm_benchmark_all ${_all_targets}) + endif () +endfunction() diff --git a/cmake/helpers/CcmExampleHelpers.cmake b/cmake/helpers/CcmExampleHelpers.cmake new file mode 100644 index 00000000..a2b9a9a7 --- /dev/null +++ b/cmake/helpers/CcmExampleHelpers.cmake @@ -0,0 +1,56 @@ +# Example executable registration (showcase-style layout, registry-driven). + +include_guard(GLOBAL) + +if (NOT DEFINED CCMATH_EXAMPLE_ROOT) + message(FATAL_ERROR "CCMATH_EXAMPLE_ROOT must be set before including CcmExampleHelpers.cmake") +endif () + +function(ccmath_add_example_target ID) + set(options) + set(oneValueArgs MODULE CXX_STANDARD) + set(multiValueArgs SOURCES COMPILE_DEFINITIONS) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (CCM_MODULE AND NOT CCM_SOURCES) + set(CCM_SOURCES "${CCMATH_EXAMPLE_ROOT}/src/${ID}.cpp") + endif () + + if (NOT CCM_SOURCES) + message(FATAL_ERROR "ccmath_add_example_target(${ID}): SOURCES or MODULE is required") + endif () + + set(_target ccmath-example-${ID}) + add_executable(${_target} ${CCM_SOURCES}) + target_link_libraries(${_target} PRIVATE ccmath::ccmath) + target_include_directories(${_target} PRIVATE "${CCMATH_EXAMPLE_ROOT}/common" "${CCMATH_EXAMPLE_ROOT}") + set_property(TARGET ${_target} PROPERTY FOLDER "examples") + if (CCM_CXX_STANDARD) + target_compile_features(${_target} PRIVATE cxx_std_${CCM_CXX_STANDARD}) + else () + set_property(TARGET ${_target} PROPERTY CXX_STANDARD 17) + endif () + + if (CCM_COMPILE_DEFINITIONS) + target_compile_definitions(${_target} PRIVATE ${CCM_COMPILE_DEFINITIONS}) + endif () + + set(${_target}_REGISTERED TRUE PARENT_SCOPE) +endfunction() + +function(ccmath_apply_example_registry) + set(_all_targets "") + foreach (_module IN LISTS CCMATH_EXAMPLE_MODULE_ORDER) + set(_examples_var CCMATH_EXAMPLE_MODULE_${_module}_EXAMPLES) + foreach (_example IN LISTS ${_examples_var}) + ccmath_add_example_target(${_example} MODULE ${_module}) + list(APPEND _all_targets ccmath-example-${_example}) + endforeach () + endforeach () + + if (_all_targets) + add_custom_target(ccmath-example-all) + add_dependencies(ccmath-example-all ${_all_targets}) + set_property(TARGET ccmath-example-all PROPERTY FOLDER "examples") + endif () +endfunction() diff --git a/cmake/helpers/CcmFuzzHelpers.cmake b/cmake/helpers/CcmFuzzHelpers.cmake new file mode 100644 index 00000000..e10480f5 --- /dev/null +++ b/cmake/helpers/CcmFuzzHelpers.cmake @@ -0,0 +1,129 @@ +# libFuzzer target registration (LLVM-libc style: shared harness, per-module drivers). +# +# Requires CCMATH_FUZZ_ROOT and CCMATH_FUZZ_COMPILE_FLAGS / CCMATH_FUZZ_LINK_FLAGS +# from ccmath_detect_libfuzzer() before include. + +include_guard(GLOBAL) + +if (NOT DEFINED CCMATH_FUZZ_ROOT) + message(FATAL_ERROR "CCMATH_FUZZ_ROOT must be set before including CcmFuzzHelpers.cmake") +endif () + +set(CCMATH_FUZZ_SHARED_SEED_DIR "${CCMATH_FUZZ_ROOT}/seeds/common") +set(CCMATH_FUZZ_DICT "${CCMATH_FUZZ_ROOT}/dicts/ccmath.dict") + +function(ccmath_fuzz_default_source MODULE OUT_VAR) + if (MODULE STREQUAL "ext") + set(_source "${CCMATH_FUZZ_ROOT}/src/ext/${MODULE}_fuzz.cpp") + elseif (MODULE MATCHES "^internal/") + set(_source "${CCMATH_FUZZ_ROOT}/src/math/${MODULE}/${MODULE}_fuzz.cpp") + else () + set(_source "${CCMATH_FUZZ_ROOT}/src/math/${MODULE}_fuzz.cpp") + endif () + set(${OUT_VAR} ${_source} PARENT_SCOPE) +endfunction() + +function(ccmath_ensure_fuzz_seeds OUT_DIR) + if (NOT EXISTS "${OUT_DIR}/zero.bin") + file(MAKE_DIRECTORY "${OUT_DIR}") + find_program(_ccmath_fuzz_python3 NAMES python3 python REQUIRED) + execute_process( + COMMAND "${_ccmath_fuzz_python3}" + "${CCMATH_FUZZ_ROOT}/scripts/generate_seeds.py" + "${OUT_DIR}" + RESULT_VARIABLE _seed_result) + if (NOT _seed_result EQUAL 0) + message(FATAL_ERROR "Failed to generate fuzz seeds in ${OUT_DIR}") + endif () + endif () +endfunction() + +function(ccmath_add_fuzz_target TARGET) + set(options) + set(oneValueArgs MODULE) + set(multiValueArgs SOURCES) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (CCM_MODULE AND NOT CCM_SOURCES) + ccmath_fuzz_default_source(${CCM_MODULE} CCM_SOURCES) + endif () + + if (NOT CCM_SOURCES) + message(FATAL_ERROR "ccmath_add_fuzz_target(${TARGET}): SOURCES or MODULE is required") + endif () + + add_executable(${TARGET} ${CCM_SOURCES}) + target_link_libraries(${TARGET} PRIVATE ccmath::ccmath) + target_include_directories(${TARGET} PRIVATE "${CCMATH_FUZZ_ROOT}/shared") + target_compile_features(${TARGET} PRIVATE cxx_std_17) + target_compile_options(${TARGET} PRIVATE ${CCMATH_FUZZ_COMPILE_FLAGS} -Wno-everything) + if (CCMATH_FUZZ_GENERIC) + target_compile_definitions(${TARGET} PRIVATE CCMATH_FUZZ_GENERIC) + endif () + target_link_options(${TARGET} PRIVATE ${CCMATH_FUZZ_LINK_FLAGS}) + + set(_corpus_dir "${CMAKE_CURRENT_BINARY_DIR}/corpus/${TARGET}") + set(_seed_dir "${CMAKE_CURRENT_BINARY_DIR}/seeds/${TARGET}") + set(_artifact_dir "${CMAKE_CURRENT_BINARY_DIR}/artifacts/${TARGET}/") + set(_common_seed_dir "${CMAKE_CURRENT_BINARY_DIR}/seeds/common") + + if (NOT TARGET ccmath-fuzz-seeds) + ccmath_ensure_fuzz_seeds("${_common_seed_dir}") + if (EXISTS "${CCMATH_FUZZ_SHARED_SEED_DIR}") + file(GLOB _shared_seeds CONFIGURE_DEPENDS "${CCMATH_FUZZ_SHARED_SEED_DIR}/*.bin") + foreach (_seed IN LISTS _shared_seeds) + get_filename_component(_seed_name "${_seed}" NAME) + configure_file("${_seed}" "${_common_seed_dir}/${_seed_name}" COPYONLY) + endforeach () + endif () + add_custom_target(ccmath-fuzz-seeds) + endif () + + file(MAKE_DIRECTORY "${_seed_dir}") + file(GLOB _common_seed_files CONFIGURE_DEPENDS "${_common_seed_dir}/*.bin") + foreach (_seed IN LISTS _common_seed_files) + get_filename_component(_seed_name "${_seed}" NAME) + configure_file("${_seed}" "${_seed_dir}/${_seed_name}" COPYONLY) + endforeach () + add_dependencies(${TARGET} ccmath-fuzz-seeds) + + add_test( + NAME ${TARGET}-smoke + COMMAND ${CMAKE_COMMAND} + -DCORPUS_DIR=${_corpus_dir} + -DSEED_DIR=${_seed_dir} + -DARTIFACT_PREFIX=${_artifact_dir} + -DFUZZER=$ + -DCCMATH_FUZZ_DICT=${CCMATH_FUZZ_DICT} + -P ${CCMATH_FUZZ_ROOT}/cmake/run_fuzz_smoke.cmake) + set_tests_properties(${TARGET}-smoke PROPERTIES TIMEOUT 300 LABELS "fuzz") + + add_custom_target(${TARGET}-campaign + COMMAND ${CMAKE_COMMAND} + -DFUZZER=$ + -DCORPUS_DIR=${_corpus_dir} + -DSEED_DIR=${_seed_dir} + -DARTIFACT_PREFIX=${_artifact_dir} + -DCCMATH_FUZZ_DICT=${CCMATH_FUZZ_DICT} + -P ${CCMATH_FUZZ_ROOT}/cmake/run_fuzz_campaign.cmake + USES_TERMINAL + COMMENT "Running libFuzzer campaign for ${TARGET}") + add_dependencies(${TARGET}-campaign ${TARGET}) +endfunction() + +function(ccmath_add_fuzz_module TARGET) + set(options) + set(oneValueArgs MODULE) + set(multiValueArgs SOURCES) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT CCM_MODULE) + message(FATAL_ERROR "ccmath_add_fuzz_module(${TARGET}): MODULE is required") + endif () + + if (CCM_SOURCES) + ccmath_add_fuzz_target(${TARGET} MODULE ${CCM_MODULE} SOURCES ${CCM_SOURCES}) + else () + ccmath_add_fuzz_target(${TARGET} MODULE ${CCM_MODULE}) + endif () +endfunction() diff --git a/cmake/helpers/CcmOptionalTooling.cmake b/cmake/helpers/CcmOptionalTooling.cmake new file mode 100644 index 00000000..6d25b5d5 --- /dev/null +++ b/cmake/helpers/CcmOptionalTooling.cmake @@ -0,0 +1,82 @@ +# Optional proof-tool discovery for tests/rigorous proof smoke targets. + +function(ccmath_find_optional_proof_tools) + find_package(Python3 COMPONENTS Interpreter QUIET) + find_program(CCMATH_GAPPA_EXECUTABLE gappa) + find_program(CCMATH_SOLLYA_EXECUTABLE sollya) + + # FindPython3 exposes these as regular variables, so lift them out of the + # helper scope before downstream checks and custom targets consume them. + set(Python3_Interpreter_FOUND "${Python3_Interpreter_FOUND}" PARENT_SCOPE) + set(Python3_EXECUTABLE "${Python3_EXECUTABLE}" PARENT_SCOPE) +endfunction() + +function(ccmath_missing_optional_tools OUT_VAR) + ccmath_find_optional_proof_tools() + set(_missing "") + if (NOT Python3_Interpreter_FOUND AND NOT Python3_EXECUTABLE) + list(APPEND _missing "Python3") + endif () + if (NOT CCMATH_GAPPA_EXECUTABLE) + list(APPEND _missing "gappa") + endif () + if (NOT CCMATH_SOLLYA_EXECUTABLE) + list(APPEND _missing "sollya") + endif () + set(${OUT_VAR} "${_missing}" PARENT_SCOPE) +endfunction() + +function(ccmath_warn_missing_optional_tools CONTEXT) + ccmath_missing_optional_tools(_missing) + if (_missing) + string(REPLACE ";" ", " _missing_text "${_missing}") + message(WARNING "CCMath ${CONTEXT}: ${_missing_text} not found; target skipped") + endif () +endfunction() + +function(ccmath_add_pow_proof_smoke_target) + ccmath_missing_optional_tools(_missing) + ccmath_warn_missing_optional_tools("pow proof smoke") + + set(_script ${CMAKE_CURRENT_SOURCE_DIR}/../proofs/math/power/proof_freshness_check.py) + set(_workdir ${CMAKE_CURRENT_SOURCE_DIR}/..) + + if (_missing) + string(REPLACE ";" ", " _missing_text "${_missing}") + add_custom_target(ccmath-rigorous-pow-proof-smoke + COMMAND ${CMAKE_COMMAND} -E echo "error: ${_missing_text} not found" + COMMAND ${CMAKE_COMMAND} -E false + COMMENT "pow proof smoke (${_missing_text} missing)") + else () + add_custom_target(ccmath-rigorous-pow-proof-smoke + COMMAND ${Python3_EXECUTABLE} ${_script} --smoke-only --require-tools + WORKING_DIRECTORY ${_workdir} + COMMENT "pow proof smoke") + endif () +endfunction() + +function(ccmath_add_pow_proof_ulp_aspiration_target) + ccmath_missing_optional_tools(_missing) + ccmath_warn_missing_optional_tools("pow proof ULP aspiration") + + set(_script ${CMAKE_CURRENT_SOURCE_DIR}/../proofs/math/power/proof_freshness_check.py) + set(_workdir ${CMAKE_CURRENT_SOURCE_DIR}/..) + + if (_missing) + string(REPLACE ";" ", " _missing_text "${_missing}") + add_custom_target(ccmath-rigorous-pow-proof-ulp-aspiration + COMMAND ${CMAKE_COMMAND} -E echo "ccmath-rigorous-pow-proof-ulp-aspiration skipped: ${_missing_text} not found" + COMMENT "pow proof ULP aspiration (${_missing_text} missing)") + else () + add_custom_target(ccmath-rigorous-pow-proof-ulp-aspiration + COMMAND ${Python3_EXECUTABLE} ${_script} --aspirational-ulp + WORKING_DIRECTORY ${_workdir} + COMMENT "pow proof aspirational 0.5 and 1.5 ULP checks (non-blocking)") + + add_test(NAME ccmath-rigorous-pow-proof-ulp-aspiration + COMMAND ${Python3_EXECUTABLE} ${_script} --aspirational-ulp + WORKING_DIRECTORY ${_workdir}) + set_tests_properties(ccmath-rigorous-pow-proof-ulp-aspiration PROPERTIES + LABELS "aspirational;proof") + endif () +endfunction() diff --git a/cmake/helpers/CcmOracleHelpers.cmake b/cmake/helpers/CcmOracleHelpers.cmake new file mode 100644 index 00000000..69498df4 --- /dev/null +++ b/cmake/helpers/CcmOracleHelpers.cmake @@ -0,0 +1,115 @@ +# Rigorous oracle campaign registration (registry-driven executables and ctests). +# +# Include OracleCampaignRegistry.cmake before calling ccmath_apply_oracle_registries(). + +include_guard(GLOBAL) + +function(_ccmath_oracle_test_requires TEST_PREFIX OUT_VAR) + set(_requires_var ${TEST_PREFIX}_REQUIRES) + if (DEFINED ${_requires_var}) + set(${OUT_VAR} ${${_requires_var}} PARENT_SCOPE) + else () + set(${OUT_VAR} "" PARENT_SCOPE) + endif () +endfunction() + +function(_ccmath_oracle_test_condition_ok REQUIRES_EXPR) + if (REQUIRES_EXPR STREQUAL "") + set(_ok TRUE PARENT_SCOPE) + return () + endif () + if (${REQUIRES_EXPR}) + set(_ok TRUE PARENT_SCOPE) + else () + set(_ok FALSE PARENT_SCOPE) + endif () +endfunction() + +function(_ccmath_oracle_register_ctest BACKEND TEST_ID LOG_DIR) + set(_prefix CCMATH_ORACLE_${BACKEND}_TEST_${TEST_ID}) + + if (NOT DEFINED ${_prefix}_NAME) + message(FATAL_ERROR "Oracle registry: missing ${_prefix}_NAME") + endif () + if (NOT DEFINED ${_prefix}_TARGET) + message(FATAL_ERROR "Oracle registry: missing ${_prefix}_TARGET") + endif () + + _ccmath_oracle_test_requires(${_prefix} _requires) + _ccmath_oracle_test_condition_ok("${_requires}" _register) + if (NOT _register) + return () + endif () + + set(_ctest_args) + if (DEFINED ${_prefix}_ARGS) + list(APPEND _ctest_args ${${_prefix}_ARGS}) + endif () + + set(_log_var ${_prefix}_LOG_OUTPUT) + set(_json_var ${_prefix}_JSON_OUTPUT) + if (DEFINED ${_log_var}) + ccmath_add_oracle_ctest(${${_prefix}_NAME} + TARGET ${${_prefix}_TARGET} + LABELS ${${_prefix}_LABELS} + TIMEOUT ${${_prefix}_TIMEOUT} + ARGS ${_ctest_args} + LOG_OUTPUT ${${_log_var}} + WORKING_DIRECTORY ${LOG_DIR}) + elseif (DEFINED ${_json_var}) + ccmath_add_oracle_ctest(${${_prefix}_NAME} + TARGET ${${_prefix}_TARGET} + LABELS ${${_prefix}_LABELS} + TIMEOUT ${${_prefix}_TIMEOUT} + ARGS ${_ctest_args} + JSON_OUTPUT ${${_json_var}} + WORKING_DIRECTORY ${LOG_DIR}) + else () + ccmath_add_oracle_ctest(${${_prefix}_NAME} + TARGET ${${_prefix}_TARGET} + LABELS ${${_prefix}_LABELS} + TIMEOUT ${${_prefix}_TIMEOUT} + ARGS ${_ctest_args} + WORKING_DIRECTORY ${LOG_DIR}) + endif () +endfunction() + +function(_ccmath_oracle_register_backend BACKEND LOG_DIR) + set(_exe_ids_var CCMATH_ORACLE_${BACKEND}_EXECUTABLE_IDS) + if (NOT DEFINED ${_exe_ids_var}) + return () + endif () + + foreach (_exe_id IN LISTS ${_exe_ids_var}) + set(_target_var CCMATH_ORACLE_${BACKEND}_${_exe_id}_TARGET) + set(_source_var CCMATH_ORACLE_${BACKEND}_${_exe_id}_SOURCE) + if (NOT DEFINED ${_target_var} OR NOT DEFINED ${_source_var}) + message(FATAL_ERROR "Oracle registry: incomplete entry for ${BACKEND} ${_exe_id}") + endif () + + if (BACKEND STREQUAL "MPFR") + ccmath_ensure_mpfr_oracle_target(${${_target_var}} SOURCES ${${_source_var}}) + elseif (BACKEND STREQUAL "COREMATH") + ccmath_ensure_coremath_oracle_target(${${_target_var}} SOURCES ${${_source_var}}) + else () + message(FATAL_ERROR "Unknown oracle backend ${BACKEND}") + endif () + + set(_ctest_ids_var CCMATH_ORACLE_${BACKEND}_${_exe_id}_CTESTS) + if (DEFINED ${_ctest_ids_var}) + foreach (_test_id IN LISTS ${_ctest_ids_var}) + _ccmath_oracle_register_ctest(${BACKEND} ${_test_id} ${LOG_DIR}) + endforeach () + endif () + endforeach () +endfunction() + +function(ccmath_apply_oracle_registries LOG_DIR) + if (CCMATH_ENABLE_MPFR_TESTS AND CCMATH_MPFR_FOUND) + _ccmath_oracle_register_backend(MPFR ${LOG_DIR}) + endif () + + if (CCMATH_ENABLE_COREMATH_TESTS AND CCMATH_COREMATH_FOUND AND TARGET ccmath::coremath_oracle) + _ccmath_oracle_register_backend(COREMATH ${LOG_DIR}) + endif () +endfunction() diff --git a/cmake/helpers/CcmTestHelpers.cmake b/cmake/helpers/CcmTestHelpers.cmake new file mode 100644 index 00000000..451ac3cd --- /dev/null +++ b/cmake/helpers/CcmTestHelpers.cmake @@ -0,0 +1,283 @@ +# Helpers for registering ccmath GTest suites, oracle executables, and script tests. +# Layout follows LLVM-libc style: sources under tests/src mirror include/ccmath, +# shared harness under tests/shared, wiring under tests/unit and tests/rigorous. + +include_guard(GLOBAL) + +if (NOT DEFINED CCMATH_TESTS_ROOT) + message(FATAL_ERROR "CCMATH_TESTS_ROOT must be set before including CcmTestHelpers.cmake") +endif () + +function(ccmath_module_rel_dir MODULE OUT_VAR) + if (MODULE STREQUAL "ext") + set(_rel src/ext) + elseif (MODULE MATCHES "^internal/") + set(_rel src/math/${MODULE}) + else () + set(_rel src/math/${MODULE}) + endif () + set(${OUT_VAR} ${_rel} PARENT_SCOPE) +endfunction() + +function(ccmath_collect_test_sources SOURCE_DIR OUT_VAR) + set(options) + set(oneValueArgs) + set(multiValueArgs EXCLUDE PATTERNS) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (IS_ABSOLUTE "${SOURCE_DIR}") + set(_abs_dir "${SOURCE_DIR}") + else () + set(_abs_dir "${CCMATH_TESTS_ROOT}/${SOURCE_DIR}") + endif () + + if (NOT EXISTS "${_abs_dir}") + message(FATAL_ERROR "ccmath_collect_test_sources: directory does not exist: ${_abs_dir}") + endif () + + if (NOT CCM_PATTERNS) + set(CCM_PATTERNS "*_test.cpp" "lerp_test_*.cpp") + endif () + + set(_candidates "") + foreach (_pattern IN LISTS CCM_PATTERNS) + file(GLOB _matches + LIST_DIRECTORIES false + RELATIVE "${CCMATH_TESTS_ROOT}" + "${_abs_dir}/${_pattern}") + list(APPEND _candidates ${_matches}) + endforeach () + + if (CCM_EXCLUDE) + foreach (_path IN LISTS CCM_EXCLUDE) + list(REMOVE_ITEM _candidates "${_path}") + endforeach () + endif () + + list(REMOVE_DUPLICATES _candidates) + list(SORT _candidates) + set(${OUT_VAR} ${_candidates} PARENT_SCOPE) +endfunction() + +function(ccmath_prefix_test_sources REL_PREFIX SOURCES OUT_VAR) + set(_prefixed "") + foreach (_source IN LISTS SOURCES) + list(APPEND _prefixed "${REL_PREFIX}${_source}") + endforeach () + set(${OUT_VAR} ${_prefixed} PARENT_SCOPE) +endfunction() + +function(ccmath_add_gtest_suite TARGET) + set(options) + set(oneValueArgs) + set(multiValueArgs SOURCES LABELS COMPILE_DEFINITIONS COMPILE_OPTIONS) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT CCM_SOURCES) + message(FATAL_ERROR "ccmath_add_gtest_suite(${TARGET}): SOURCES is required") + endif () + + add_executable(${TARGET}) + target_sources(${TARGET} PRIVATE ${CCM_SOURCES}) + target_link_libraries(${TARGET} PRIVATE ccmath::test) + add_test(NAME ${TARGET} COMMAND ${TARGET}) + + if (CCM_LABELS) + set_tests_properties(${TARGET} PROPERTIES LABELS "${CCM_LABELS}") + endif () + if (CCM_COMPILE_DEFINITIONS) + target_compile_definitions(${TARGET} PRIVATE ${CCM_COMPILE_DEFINITIONS}) + endif () + if (CCM_COMPILE_OPTIONS) + target_compile_options(${TARGET} PRIVATE ${CCM_COMPILE_OPTIONS}) + endif () +endfunction() + +function(ccmath_add_command_gtest TARGET) + set(options) + set(oneValueArgs) + set(multiValueArgs SOURCES LABELS COMPILE_DEFINITIONS COMPILE_OPTIONS) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT CCM_SOURCES) + message(FATAL_ERROR "ccmath_add_command_gtest(${TARGET}): SOURCES is required") + endif () + + add_executable(${TARGET} ${CCM_SOURCES}) + target_link_libraries(${TARGET} PRIVATE ccmath::ccmath ccmath::ext) + target_include_directories(${TARGET} PRIVATE + ${CCMATH_TESTS_ROOT} + ${CCMATH_TESTS_ROOT}/shared + ${CCMATH_TESTS_ROOT}/..) + target_compile_features(${TARGET} PRIVATE cxx_std_17) + add_test(NAME ${TARGET} COMMAND ${TARGET}) + + if (CCM_LABELS) + set_tests_properties(${TARGET} PROPERTIES LABELS "${CCM_LABELS}") + endif () + if (CCM_COMPILE_DEFINITIONS) + target_compile_definitions(${TARGET} PRIVATE ${CCM_COMPILE_DEFINITIONS}) + endif () + if (CCM_COMPILE_OPTIONS) + target_compile_options(${TARGET} PRIVATE ${CCM_COMPILE_OPTIONS}) + endif () +endfunction() + +function(ccmath_add_unit_module) + set(options) + set(oneValueArgs TARGET MODULE) + set(multiValueArgs LABELS EXTRA_SOURCES EXCLUDE PATTERNS) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT CCM_TARGET OR NOT CCM_MODULE) + message(FATAL_ERROR "ccmath_add_unit_module requires TARGET and MODULE") + endif () + + ccmath_module_rel_dir(${CCM_MODULE} _rel_dir) + ccmath_collect_test_sources(${_rel_dir} _sources + EXCLUDE ${CCM_EXCLUDE} + PATTERNS ${CCM_PATTERNS}) + ccmath_prefix_test_sources("../" "${_sources}" _prefixed) + + if (CCM_EXTRA_SOURCES) + list(APPEND _prefixed ${CCM_EXTRA_SOURCES}) + endif () + + if (NOT _prefixed) + message(FATAL_ERROR "ccmath_add_unit_module(${CCM_TARGET}): no test sources in ${_rel_dir}") + endif () + + ccmath_add_gtest_suite(${CCM_TARGET} SOURCES ${_prefixed} LABELS ${CCM_LABELS}) +endfunction() + +function(ccmath_add_rigorous_gtest_suite TARGET) + set(options APPLY_POW_VALIDATION) + set(oneValueArgs) + set(multiValueArgs SOURCES LABELS COMPILE_DEFINITIONS COMPILE_OPTIONS) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT CCM_SOURCES) + message(FATAL_ERROR "ccmath_add_rigorous_gtest_suite(${TARGET}): SOURCES is required") + endif () + + if (NOT CCM_LABELS) + set(CCM_LABELS rigorous) + endif () + + ccmath_add_gtest_suite(${TARGET} + SOURCES ${CCM_SOURCES} + LABELS ${CCM_LABELS} + COMPILE_DEFINITIONS ${CCM_COMPILE_DEFINITIONS} + COMPILE_OPTIONS ${CCM_COMPILE_OPTIONS}) + + if (CCM_APPLY_POW_VALIDATION) + if (NOT COMMAND ccmath_apply_pow_validation_options) + message(FATAL_ERROR "ccmath_add_rigorous_gtest_suite(${TARGET}): include TestValidationOptions.cmake first") + endif () + ccmath_apply_pow_validation_options(${TARGET}) + endif () +endfunction() + +function(ccmath_add_rigorous_module_suite TARGET) + set(options APPLY_POW_VALIDATION) + set(oneValueArgs MODULE) + set(multiValueArgs LABELS EXCLUDE PATTERNS EXTRA_SOURCES) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT CCM_MODULE) + message(FATAL_ERROR "ccmath_add_rigorous_module_suite(${TARGET}): MODULE is required") + endif () + + ccmath_module_rel_dir(${CCM_MODULE} _rel_dir) + ccmath_collect_test_sources(${_rel_dir} _sources + EXCLUDE ${CCM_EXCLUDE} + PATTERNS ${CCM_PATTERNS}) + ccmath_prefix_test_sources("../" "${_sources}" _prefixed) + + if (CCM_EXTRA_SOURCES) + list(APPEND _prefixed ${CCM_EXTRA_SOURCES}) + endif () + + if (NOT _prefixed) + message(FATAL_ERROR "ccmath_add_rigorous_module_suite(${TARGET}): no test sources in ${_rel_dir}") + endif () + + if (CCM_APPLY_POW_VALIDATION) + ccmath_add_rigorous_gtest_suite(${TARGET} + SOURCES ${_prefixed} + LABELS ${CCM_LABELS} + APPLY_POW_VALIDATION) + else () + ccmath_add_rigorous_gtest_suite(${TARGET} + SOURCES ${_prefixed} + LABELS ${CCM_LABELS}) + endif () +endfunction() + +function(ccmath_add_conformance_suite TARGET) + set(options APPLY_POW_VALIDATION) + set(oneValueArgs) + set(multiValueArgs EXCLUDE EXTRA_SOURCES) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + ccmath_collect_test_sources(conformance _sources EXCLUDE ${CCM_EXCLUDE}) + ccmath_prefix_test_sources("../" "${_sources}" _prefixed) + + if (CCM_EXTRA_SOURCES) + list(APPEND _prefixed ${CCM_EXTRA_SOURCES}) + endif () + + if (CCM_APPLY_POW_VALIDATION) + ccmath_add_rigorous_gtest_suite(${TARGET} + SOURCES ${_prefixed} + APPLY_POW_VALIDATION) + else () + ccmath_add_rigorous_gtest_suite(${TARGET} SOURCES ${_prefixed}) + endif () +endfunction() + +function(ccmath_add_python_script_test TEST_NAME) + set(options OPTIONAL) + set(oneValueArgs SCRIPT WORKING_DIRECTORY TIMEOUT) + set(multiValueArgs LABELS ARGS) + cmake_parse_arguments(CCM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT CCM_SCRIPT) + message(FATAL_ERROR "ccmath_add_python_script_test(${TEST_NAME}): SCRIPT is required") + endif () + if (NOT Python3_EXECUTABLE) + if (CCM_OPTIONAL) + message(WARNING "CCMath: Python3 not found; ${TEST_NAME} skipped") + return () + endif () + message(FATAL_ERROR "ccmath_add_python_script_test(${TEST_NAME}): Python3 not available") + endif () + + set(_command ${Python3_EXECUTABLE} ${CCM_SCRIPT}) + if (CCM_ARGS) + list(APPEND _command ${CCM_ARGS}) + endif () + + if (NOT CCM_WORKING_DIRECTORY) + set(CCM_WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + endif () + + add_test(NAME ${TEST_NAME} COMMAND ${_command}) + set_tests_properties(${TEST_NAME} PROPERTIES WORKING_DIRECTORY "${CCM_WORKING_DIRECTORY}") + + if (CCM_LABELS) + set_tests_properties(${TEST_NAME} PROPERTIES LABELS "${CCM_LABELS}") + endif () + if (CCM_TIMEOUT) + set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT ${CCM_TIMEOUT}) + endif () +endfunction() + +# Back-compat aliases used by older CMakeLists fragments. +function(ccmath_add_test TEST_TARGET) + ccmath_add_gtest_suite(${TEST_TARGET} SOURCES ${ARGN}) +endfunction() + +function(ccmath_add_command_test TEST_TARGET) + ccmath_add_command_gtest(${TEST_TARGET} SOURCES ${ARGN}) +endfunction() diff --git a/cmake/modules/CcmLibFuzzer.cmake b/cmake/modules/CcmLibFuzzer.cmake index fb946a12..db70a590 100644 --- a/cmake/modules/CcmLibFuzzer.cmake +++ b/cmake/modules/CcmLibFuzzer.cmake @@ -15,12 +15,22 @@ set(CCMATH_FUZZ_SANITIZERS "fuzzer,address,undefined") function(ccmath_probe_libfuzzer compile_flags link_flags result_var) set(CMAKE_REQUIRED_FLAGS ${compile_flags}) set(CMAKE_REQUIRED_LINK_OPTIONS ${link_flags}) - check_cxx_source_compiles(" + # With -fsanitize=fuzzer the runtime supplies main(). With fuzzer-no-link, the probe needs its own. + if (compile_flags MATCHES "fuzzer-no-link") + set(_probe_source " #include #include extern \"C\" int LLVMFuzzerTestOneInput(uint8_t const*, size_t) { return 0; } int main() { return 0; } -" ${result_var}) +") + else () + set(_probe_source " +#include +#include +extern \"C\" int LLVMFuzzerTestOneInput(uint8_t const*, size_t) { return 0; } +") + endif () + check_cxx_source_compiles("${_probe_source}" ${result_var}) endfunction() function(ccmath_append_compiler_libcxx_flags compile_flags_var link_flags_var) @@ -47,7 +57,7 @@ function(ccmath_append_compiler_libcxx_flags compile_flags_var link_flags_var) endfunction() function(ccmath_detect_libfuzzer) - set(_compile_base "-fno-omit-frame-pointer" "-O1") + set(_compile_base "-fno-omit-frame-pointer" "-O1" "-fno-sanitize-recover=all") set(_available FALSE) if (DEFINED ENV{LIB_FUZZING_ENGINE}) diff --git a/cmake/vcpkg/vcpkg.json b/cmake/vcpkg/vcpkg.json new file mode 100644 index 00000000..34ec0a9f --- /dev/null +++ b/cmake/vcpkg/vcpkg.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", + "name": "ccmath-validation", + "version-string": "0.3.0", + "builtin-baseline": "10ceb139a610ebf3c6aa49cdc4a4b7f3db5d3f2b", + "dependencies": [ + "mpfr", + "pkgconf" + ] +} diff --git a/docs/ROUNDING_STATUS.md b/docs/ROUNDING_STATUS.md new file mode 100644 index 00000000..591b8bf8 --- /dev/null +++ b/docs/ROUNDING_STATUS.md @@ -0,0 +1,91 @@ +## Rounding Status + +Status for the four IEEE rounding modes. x = runtime handles that mode. Empty = not applicable or not implemented. + +| Feature | FE_TONEAREST? | FE_UPWARD? | FE_DOWNWARD? | FE_TOWARDZERO? | Notes? | +|----------------|---------------|------------|--------------|----------------|------------------------------------------------------------------------------------------------------------------------------------------| +| fabs | x | x | x | x | | +| fdim | x | x | x | x | | +| fma | x | x | x | x | Native-first on validated AArch64 runtime paths; constexpr follows `CCM_CONSTEXPR_ROUNDING_MODE`; `fmal` remains target-format dependent | +| (f)max | x | x | x | x | | +| (f)min | x | x | x | x | | +| remainder | x | x | x | x | | +| remquo | x | x | x | x | | +| nan | | | | | | +| fpclassify | | | | | | +| isfinite | | | | | | +| isgreater | | | | | | +| isgreaterequal | | | | | | +| isinf | | | | | | +| isless | | | | | | +| islessequal | | | | | | +| islessgreater | | | | | | +| isnan | | | | | | +| isnormal | | | | | | +| isunordered | | | | | | +| signbit | | | | | | +| exp | x | x | x | x | | +| exp2 | x | x | x | x | | +| expm1 | x | x | x | x | | +| log | x | x | x | x | | +| log1p | x | x | x | x | | +| log2 | x | x | x | x | | +| log10 | x | x | x | x | | +| copysign | x | x | x | x | | +| frexp | x | x | x | x | | +| ilogb | x | x | x | x | | +| ldexp | x | x | x | x | | +| logb | x | x | x | x | | +| modf | x | x | x | x | | +| nextafter | x | x | x | x | | +| nexttoward | x | x | x | x | | +| scalbn | x | x | x | x | | +| acosh | | | | | | +| asinh | | | | | | +| atanh | | | | | | +| cosh | | | | | | +| sinh | | | | | | +| tanh | | | | | | +| ceil | x | x | x | x | | +| floor | x | x | x | x | | +| nearbyint | x | x | x | x | | +| rint | x | x | x | x | | +| round | x | x | x | x | | +| trunc | x | x | x | x | | +| cbrt | x | x | x | x | | +| hypot | x | x | x | x | | +| pow | x | x | x | x | CORE-MATH all-mode campaigns on covered inputs | +| sqrt | x | x | x | x | | +| assoc_laguerre | | | | | | +| assoc_legendre | | | | | | +| beta | | | | | | +| comp_ellint_1 | | | | | | +| comp_ellint_2 | | | | | | +| comp_ellint_3 | | | | | | +| cyl_bessel_i | | | | | | +| cyl_bessel_j | | | | | | +| cyl_bessel_k | | | | | | +| cyl_neumann | | | | | | +| ellint_1 | | | | | | +| ellint_2 | | | | | | +| ellint_3 | | | | | | +| expint | | | | | | +| hermite | | | | | | +| laguerre | | | | | | +| legendre | | | | | | +| riemann_zeta | | | | | | +| sph_bessel | | | | | | +| sph_legendre | | | | | | +| sph_neumann | | | | | | +| acos | x | x | x | x | | +| asin | x | x | x | x | | +| atan | x | x | x | x | | +| atan2 | x | x | x | x | | +| cos | x | x | x | x | | +| sin | x | x | x | x | | +| tan | x | x | x | x | | +| gamma | x | x | x | x | | +| lerp | x | x | x | x | | +| lgamma | x | x | x | x | | + +> Last Updated: June 7, 2026 diff --git a/docs/STATUS.md b/docs/STATUS.md index 7eceae16..216f6fce 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -1,107 +1,108 @@ ## Implementation Progress (Modules) -| Module | % done | In Progress? | Notes? | Planned Completion Version | -|------------------------------|--------|--------------|------------------------------------------|----------------------------| -| Basic | 100 | | remquo `long double` delegates to double | v0.1.0 (Released) | -| Compare | 100 | | | v0.1.0 (Released) | -| Exponential | 98 | | | v0.2.0 (Released) | -| Float Manipulation | 100 | | scalbn `long double` is slow | v0.2.0 (Released) | -| Hyperbolic | 0 | | Empty headers only | | -| Nearest | 100 | | | v0.2.0 (Released) | -| Power | 96 | | | v0.2.0 (Released) | -| Special Functions | 0 | | Empty headers only | | -| Trigonometric | 100 | | | v0.2.0 (Released) | -| Misc Functions | 97 | | | v0.2.0 (Released) | -| **Total Library Completion** | **68** | | | v0.2.0 (Released) | +| Module | % done | In Progress? | Notes? | Planned Completion Version | +|------------------------------|--------|--------------|------------------------------------------------------------------------------------------|----------------------------| +| Basic | 100 | | remquo `long double` delegates to double | v0.1.0 (Released) | +| Compare | 100 | | | v0.1.0 (Released) | +| Exponential | 98 | | | v0.2.0 (Released) | +| Float Manipulation | 100 | | scalbn `long double` is slow | v0.2.0 (Released) | +| Hyperbolic | 0 | | Empty headers only | | +| Nearest | 100 | | | v0.2.0 (Released) | +| Power | 96 | | Path forcing, MPFR path-matrix, powf reduced domains, powl characterization, proof-smoke | v0.2.0 (Released) | +| Special Functions | 0 | | Empty headers only | | +| Trigonometric | 100 | | | v0.2.0 (Released) | +| Misc Functions | 97 | | | v0.2.0 (Released) | +| **Total Library Completion** | **68** | | | N/a | -> Last Updated: May 31, 2026 +> Last Updated: June 7, 2026 ## Implementation Progress (Functions) -| Feature | % done | TODO | -|----------------|--------|----------------------------------| -| fabs | 100 | | -| fdim | 100 | | -| fma | 100 | | -| (f)max | 100 | | -| (f)min | 100 | | -| remainder | 100 | | -| remquo | 100 | remquol delegates through double | -| nan | 100 | Brittle on non-IEC559 platforms | -| fpclassify | 100 | | -| isfinite | 100 | | -| isgreater | 100 | | -| isgreaterequal | 100 | | -| isinf | 100 | | -| isless | 100 | | -| islessequal | 100 | | -| islessgreater | 100 | | -| isnan | 100 | | -| isnormal | 100 | | -| isunordered | 100 | | -| signbit | 100 | | -| exp | 98 | | -| exp2 | 98 | | -| expm1 | 98 | | -| log | 98 | | -| log1p | 98 | | -| log2 | 98 | | -| log10 | 98 | | -| copysign | 100 | | -| frexp | 100 | | -| ilogb | 100 | | -| ldexp | 100 | | -| logb | 100 | | -| modf | 100 | | -| nextafter | 100 | | -| nexttoward | 100 | | -| scalbn | 100 | Long double is slow. | -| acosh | 0 | Empty header | -| asinh | 0 | Empty header | -| atanh | 0 | Empty header | -| cosh | 0 | Empty header | -| sinh | 0 | Empty header | -| tanh | 0 | Empty header | -| ceil | 100 | | -| floor | 100 | | -| nearbyint | 100 | Runtime rounding mode via `fenv` | -| rint | 100 | | -| round | 100 | | -| trunc | 100 | | -| cbrt | 98 | | -| hypot | 100 | | -| pow | 88 | | -| sqrt | 100 | | -| assoc_laguerre | 0 | Empty header | -| assoc_legendre | 0 | Empty header | -| beta | 0 | Empty header | -| comp_ellint_1 | 0 | Empty header | -| comp_ellint_2 | 0 | Empty header | -| comp_ellint_3 | 0 | Empty header | -| cyl_bessel_i | 0 | Empty header | -| cyl_bessel_j | 0 | Empty header | -| cyl_bessel_k | 0 | Empty header | -| cyl_neumann | 0 | Empty header | -| ellint_1 | 0 | Empty header | -| ellint_2 | 0 | Empty header | -| ellint_3 | 0 | Empty header | -| expint | 0 | Empty header | -| hermite | 0 | Empty header | -| laguerre | 0 | Empty header | -| legendre | 0 | Empty header | -| riemann_zeta | 0 | Empty header | -| sph_bessel | 0 | Empty header | -| sph_legendre | 0 | Empty header | -| sph_neumann | 0 | Empty header | -| acos | 100 | | -| asin | 100 | | -| atan | 100 | | -| atan2 | 100 | | -| cos | 100 | | -| sin | 100 | | -| tan | 100 | | -| gamma | 100 | | -| lerp | 90 | | -| lgamma | 100 | | +Per-function rounding mode status: [ROUNDING_STATUS.md](ROUNDING_STATUS.md). -> Last Updated: May 31, 2026 +| Feature | % done | TODO | +|----------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| fabs | 100 | | +| fdim | 100 | | +| fma | 100 | Native-first AArch64 runtime, exact software fallback for all modes, constexpr respects `CCM_CONSTEXPR_ROUNDING_MODE`. Wider x86_64 validation remains open. | +| (f)max | 100 | | +| (f)min | 100 | | +| remainder | 100 | | +| remquo | 100 | remquol delegates through double | +| nan | 100 | Brittle on non-IEC559 platforms | +| fpclassify | 100 | | +| isfinite | 100 | | +| isgreater | 100 | | +| isgreaterequal | 100 | | +| isinf | 100 | | +| isless | 100 | | +| islessequal | 100 | | +| islessgreater | 100 | | +| isnan | 100 | | +| isnormal | 100 | | +| isunordered | 100 | | +| signbit | 100 | | +| exp | 98 | | +| exp2 | 98 | | +| expm1 | 98 | | +| log | 98 | | +| log1p | 98 | | +| log2 | 98 | | +| log10 | 98 | | +| copysign | 100 | | +| frexp | 100 | | +| ilogb | 100 | | +| ldexp | 100 | | +| logb | 100 | | +| modf | 100 | | +| nextafter | 100 | | +| nexttoward | 100 | | +| scalbn | 100 | Long double is slow. | +| acosh | 0 | Empty header | +| asinh | 0 | Empty header | +| atanh | 0 | Empty header | +| cosh | 0 | Empty header | +| sinh | 0 | Empty header | +| tanh | 0 | Empty header | +| ceil | 100 | | +| floor | 100 | | +| nearbyint | 100 | Runtime rounding mode via `fenv` | +| rint | 100 | | +| round | 100 | | +| trunc | 100 | | +| cbrt | 98 | | +| hypot | 100 | | +| pow | 88 | Rigorous MPFR `public_default` campaigns, path-matrix tooling, powf reduced domains, powl characterization. Full binary64 proof and independent extended-precision `powl` validation remain open | +| sqrt | 100 | | +| assoc_laguerre | 0 | Empty header | +| assoc_legendre | 0 | Empty header | +| beta | 0 | Empty header | +| comp_ellint_1 | 0 | Empty header | +| comp_ellint_2 | 0 | Empty header | +| comp_ellint_3 | 0 | Empty header | +| cyl_bessel_i | 0 | Empty header | +| cyl_bessel_j | 0 | Empty header | +| cyl_bessel_k | 0 | Empty header | +| cyl_neumann | 0 | Empty header | +| ellint_1 | 0 | Empty header | +| ellint_2 | 0 | Empty header | +| ellint_3 | 0 | Empty header | +| expint | 0 | Empty header | +| hermite | 0 | Empty header | +| laguerre | 0 | Empty header | +| legendre | 0 | Empty header | +| riemann_zeta | 0 | Empty header | +| sph_bessel | 0 | Empty header | +| sph_legendre | 0 | Empty header | +| sph_neumann | 0 | Empty header | +| acos | 100 | | +| asin | 100 | | +| atan | 100 | | +| atan2 | 100 | | +| cos | 100 | | +| sin | 100 | | +| tan | 100 | | +| gamma | 100 | | +| lerp | 90 | | +| lgamma | 100 | | +> Last Updated: June 7, 2026 diff --git a/docs/ULP_STATUS.md b/docs/ULP_STATUS.md new file mode 100644 index 00000000..70057152 --- /dev/null +++ b/docs/ULP_STATUS.md @@ -0,0 +1,892 @@ +## Table of Contents + +### Basic +- [fabs](#fabs) +- [fdim](#fdim) +- [fma](#fma) +- [(f)max](#fmax) +- [(f)min](#fmin) +- [remainder](#remainder) +- [remquo](#remquo) +- [nan](#nan) + +### Compare +- [fpclassify](#fpclassify) +- [isfinite](#isfinite) +- [isgreater](#isgreater) +- [isgreaterequal](#isgreaterequal) +- [isinf](#isinf) +- [isless](#isless) +- [islessequal](#islessequal) +- [islessgreater](#islessgreater) +- [isnan](#isnan) +- [isnormal](#isnormal) +- [isunordered](#isunordered) +- [signbit](#signbit) + +### Exponential +- [exp](#exp) +- [exp2](#exp2) +- [expm1](#expm1) +- [log](#log) +- [log1p](#log1p) +- [log2](#log2) +- [log10](#log10) + +### Float Manipulation +- [copysign](#copysign) +- [frexp](#frexp) +- [ilogb](#ilogb) +- [ldexp](#ldexp) +- [logb](#logb) +- [modf](#modf) +- [nextafter](#nextafter) +- [nexttoward](#nexttoward) +- [scalbn](#scalbn) + +### Hyperbolic +- [acosh](#acosh) +- [asinh](#asinh) +- [atanh](#atanh) +- [cosh](#cosh) +- [sinh](#sinh) +- [tanh](#tanh) + +### Nearest +- [ceil](#ceil) +- [floor](#floor) +- [nearbyint](#nearbyint) +- [rint](#rint) +- [round](#round) +- [trunc](#trunc) + +### Power +- [cbrt](#cbrt) +- [hypot](#hypot) +- [pow](#pow) +- [sqrt](#sqrt) + +### Special Functions +- [assoc_laguerre](#assoc_laguerre) +- [assoc_legendre](#assoc_legendre) +- [beta](#beta) +- [comp_ellint_1](#comp_ellint_1) +- [comp_ellint_2](#comp_ellint_2) +- [comp_ellint_3](#comp_ellint_3) +- [cyl_bessel_i](#cyl_bessel_i) +- [cyl_bessel_j](#cyl_bessel_j) +- [cyl_bessel_k](#cyl_bessel_k) +- [cyl_neumann](#cyl_neumann) +- [ellint_1](#ellint_1) +- [ellint_2](#ellint_2) +- [ellint_3](#ellint_3) +- [expint](#expint) +- [hermite](#hermite) +- [laguerre](#laguerre) +- [legendre](#legendre) +- [riemann_zeta](#riemann_zeta) +- [sph_bessel](#sph_bessel) +- [sph_legendre](#sph_legendre) +- [sph_neumann](#sph_neumann) + +### Trigonometric +- [acos](#acos) +- [asin](#asin) +- [atan](#atan) +- [atan2](#atan2) +- [cos](#cos) +- [sin](#sin) +- [tan](#tan) + +### Misc Functions +- [gamma](#gamma) +- [lerp](#lerp) +- [lgamma](#lgamma) + +## ULP Status + +## Basic + + +### fabs + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|---------------------------------------------------------------------------------------| +| float | N/a | N/a | N/a | See rigorous MPFR `fmaf` all-mode campaigns and simple constexpr residual regressions | +| double | N/a | N/a | N/a | See rigorous MPFR `fma` all-mode campaigns and simple constexpr residual regressions | +| long double | N/a | N/a | N/a | `fmal` remains a target-format audit path in this pass | + + +### fdim + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### fma + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### (f)max + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### (f)min + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### remainder + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### remquo + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### nan + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + +## Compare + + +### fpclassify + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### isfinite + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### isgreater + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### isgreaterequal + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### isinf + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### isless + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### islessequal + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### islessgreater + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### isnan + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### isnormal + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### isunordered + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### signbit + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + +## Exponential + + +### exp + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### exp2 + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### expm1 + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### log + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### log1p + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### log2 + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### log10 + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + +## Float Manipulation + + +### copysign + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### frexp + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### ilogb + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### ldexp + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### logb + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### modf + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### nextafter + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### nexttoward + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### scalbn + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + +## Hyperbolic + + +### acosh + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### asinh + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### atanh + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### cosh + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### sinh + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### tanh + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + +## Nearest + + +### ceil + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### floor + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### nearbyint + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### rint + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### round + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### trunc + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + +## Power + + +### cbrt + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### hypot + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### pow + +Rigorous oracle quick campaigns (apple aarch64, clang 17, June 7 2026). Not proofs. Max ULP is the same in all four IEEE rounding modes on the corpora below. + +**Failure columns** + +| Column | Meaning | +|--------|---------| +| MPFR hard failures | Cases that exceed the campaign 4 ULP ceiling, or disagree with MPFR on NaN, infinity, or signed zero. Within-ceiling results that miss the 0.5 ULP correctly-rounded target are tracked separately and are not counted here. | +| CORE-MATH bit mismatches | Cases where ccm bits differ from the CORE-MATH cr_* reference in the active rounding mode. This is not a ULP threshold. Cases where a higher-precision cross-check shows ccm is correct and the CORE-MATH oracle is wrong are excluded. | + +Per-case logs with base, exponent, actual, and expected bit patterns live under [tests/rigorous/oracle_logs/README.md](../tests/rigorous/oracle_logs/README.md). Re-run the rigorous oracle ctest targets to refresh them. + +| Function | Path / configuration | MPFR cases | MPFR Max ULP | MPFR hard failures | CORE-MATH cases | CORE-MATH Max ULP | CORE-MATH bit mismatches | Notes | +|-------------|------------------------------------------------|------------|--------------|--------------------|-----------------|-------------------|--------------------------|-----------------------------------------------------------------------------------| +| `ccm::pow` | `public_default` | 3132 | 1 | 0 | 2068 | 1 | 59 | MPFR structured binary64 corpus. CORE-MATH finite regular corpus. | +| `ccm::pow` | `runtime_no_builtin`, `generic_modeled_domain` | N/a | N/a | 0 | N/a | N/a | N/a | Path-matrix quick campaign, no-builtin build only. | +| `ccm::powf` | `public_default` | 68036 | 1 | 2 | 62372 | 1 | 2050 | Structured binary32 corpus. Directional modes carry most CORE-MATH mismatches. | +| `ccm::powf` | Reduced domains | 6144 | 1 | 0 | 6144 | 1 | 396 | Mantissa/subnormal finite domains. Not evidence for NaN, infinity, or zero cases. | +| `ccm::powl` | ld64 alias | 144 | 0 | 0 | N/a | N/a | N/a | Double-shaped platform. ld64_conservative corpus. | +| `ccm::powl` | ld80 special + bounded int + general finite | N/a | N/a | N/a | N/a | N/a | N/a | Not exercised on double-shaped platforms. | +| `ccm::powl` | ld128 / unknown strict | N/a | N/a | N/a | N/a | N/a | N/a | Detection and NaN policy tests only. | +| `ccm::powl` | reduced-precision fallback | N/a | N/a | N/a | N/a | N/a | N/a | Only when CCMATH_ENABLE_REDUCED_PRECISION_POWL=ON. | + + + +### sqrt + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + +## Special Functions + + +### assoc_laguerre + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### assoc_legendre + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### beta + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### comp_ellint_1 + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### comp_ellint_2 + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### comp_ellint_3 + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### cyl_bessel_i + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### cyl_bessel_j + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### cyl_bessel_k + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### cyl_neumann + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### ellint_1 + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### ellint_2 + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### ellint_3 + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### expint + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### hermite + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### laguerre + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### legendre + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### riemann_zeta + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### sph_bessel + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### sph_legendre + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### sph_neumann + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + +## Trigonometric + + +### acos + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### asin + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### atan + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### atan2 + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### cos + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### sin + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### tan + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + +## Misc Functions + + +### gamma + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### lerp + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + + +### lgamma + +| Precision | Min ULP | Max ULP | Avg ULP | Notes | +|-------------|---------|---------|---------|-------| +| float | N/a | N/a | N/a | | +| double | N/a | N/a | N/a | | +| long double | N/a | N/a | N/a | | + +> Last Updated: June 7, 2026 diff --git a/docs/approximating_functions/ACM-Reference-Format.bst b/docs/approximating_functions/ACM-Reference-Format.bst new file mode 100644 index 00000000..ac35934b --- /dev/null +++ b/docs/approximating_functions/ACM-Reference-Format.bst @@ -0,0 +1,3224 @@ +%%% -*-BibTeX-*- +%%% ==================================================================== +%%% @BibTeX-style-file{ +%%% author = "Nelson H. F. Beebe, Boris Veytsman and Gerald Murray", +%%% version = "2.1", +%%% acmart-version = "1.90", +%%% date = "Mar 26 2023", +%%% filename = "ACM-Reference-Format.bst", +%%% email = "borisv@lk.net, boris@varphi.com", +%%% codetable = "ISO/ASCII", +%%% keywords = "ACM Transactions bibliography style; BibTeX", +%%% license = "public domain", +%%% supported = "yes", +%%% abstract = "", +%%% } +%%% ==================================================================== + +%%% Revision history: see source in git + +ENTRY + { address + advisor + archiveprefix + author + booktitle + chapter + city + date + edition + editor + eprint + eprinttype + eprintclass + howpublished + institution + journal + key + location + month + note + number + organization + pages + primaryclass + publisher + school + series + title + type + volume + year + % New keys recognized + issue % UTAH: used in, e.g., ACM SIGSAM Bulletin and ACM Communications in Computer Algebra + articleno + eid + day % UTAH: needed for newspapers, weeklies, bi-weeklies + doi % UTAH + url % UTAH + bookpages % UTAH + numpages + lastaccessed % UTAH: used only for @Misc{...} + coden % UTAH + isbn % UTAH + isbn-13 % UTAH + issn % UTAH + lccn % UTAH + distinctURL % whether to print url if doi is present + archived % Where the url is archived + venue % for presentations + } + {} + { label.year extra.label sort.year sort.label basic.label.year} + +INTEGERS { output.state before.all mid.sentence after.sentence after.block } + +INTEGERS { show-isbn-10-and-13 } % initialized below in begin.bib + +INTEGERS { nameptr namesleft numnames } + +INTEGERS { multiresult } + +INTEGERS { len } + +INTEGERS { last.extra.num } + +STRINGS { s t t.org u } + +STRINGS { last.label next.extra } + +STRINGS { p1 p2 p3 page.count } + + +FUNCTION { not } +{ + { #0 } + { #1 } + if$ +} + +FUNCTION { and } +{ + 'skip$ + { pop$ #0 } + if$ +} + +FUNCTION { or } +{ + { pop$ #1 } + 'skip$ + if$ +} + + +FUNCTION { dump.stack.1 } +{ + duplicate$ "STACK[top] = [" swap$ * "]" * warning$ +} + +FUNCTION { dump.stack.2 } +{ + duplicate$ "STACK[top ] = [" swap$ * "]" * warning$ + swap$ + duplicate$ "STACK[top-1] = [" swap$ * "]" * warning$ + swap$ +} + +FUNCTION { empty.or.unknown } +{ + %% Examine the top stack entry, and push 1 if it is empty, or + %% consists only of whitespace, or is a string beginning with two + %% queries (??), and otherwise, push 0. + %% + %% This function provides a replacement for empty$, with the + %% convenient feature that unknown values marked by two leading + %% queries are treated the same as missing values, and thus, do not + %% appear in the output .bbl file, and yet, their presence in .bib + %% file(s) serves to mark values which are temporarily missing, but + %% are expected to be filled in eventually once more data is + %% obtained. The TeX User Group and BibNet bibliography archives + %% make extensive use of this practice. + %% + %% An empty string cannot serve the same purpose, because just as in + %% statistics data processing, an unknown value is not the same as an + %% empty value. + %% + %% At entry: stack = ... top:[string] + %% At exit: stack = ... top:[0 or 1] + + duplicate$ empty$ + { pop$ #1 } + { #1 #2 substring$ "??" = } + if$ +} + +FUNCTION { empty.or.zero } +{ + %% Examine the top entry and push 1 if it is empty, or is zero + duplicate$ empty$ + { pop$ #1 } + { "0" = } + if$ +} + + +FUNCTION { writeln } +{ + %% In BibTeX style files, the sequences + %% + %% ... "one" "two" output + %% ... "one" "two" output.xxx + %% + %% ship "one" to the output file, possibly following by punctuation, + %% leaving the stack with + %% + %% ... "two" + %% + %% There is thus a one-string lag in output processing that must be + %% carefully handled to avoid duplicating a string in the output + %% file. Unless otherwise noted, all output.xxx functions leave + %% just one new string on the stack, and that model should be born + %% in mind when reading or writing function code. + %% + %% BibTeX's asynchronous buffering of output from strings from the + %% stack is confusing because newline$ bypasses the buffer. It + %% would have been so much easier for newline to be a character + %% rather than a state of the output-in-progress. + %% + %% The documentation in btxhak.dvi is WRONG: it says + %% + %% newline$ Writes onto the bbl file what's accumulated in the + %% output buffer. It writes a blank line if and only + %% if the output buffer is empty. Since write$ does + %% reasonable line breaking, you should use this + %% function only when you want a blank line or an + %% explicit line break. + %% + %% write$ Pops the top (string) literal and writes it on the + %% output buffer (which will result in stuff being + %% written onto the bbl file when the buffer fills + %% up). + %% + %% Examination of the BibTeX source code shows that write$ does + %% indeed behave as claimed, but newline$ sends a newline character + %% directly to the output file, leaving the stack unchanged. The + %% first line "Writes onto ... buffer." is therefore wrong. + %% + %% The original BibTeX style files almost always use "write$ newline$" + %% in that order, so it makes sense to hide that pair in a private + %% function like this one, named after a statement in Pascal, + %% the programming language embedded in the BibTeX Web program. + + write$ % output top-of-stack string + newline$ % immediate write of newline (not via stack) +} + +FUNCTION { init.state.consts } +{ + #0 'before.all := + #1 'mid.sentence := + #2 'after.sentence := + #3 'after.block := +} + +FUNCTION { output.nonnull } +{ % Stack in: ... R S T Stack out: ... R T File out: S + 's := + output.state mid.sentence = + { + ", " * write$ + } + { + output.state after.block = + { + add.period$ writeln + "\newblock " write$ + } + { + output.state before.all = + { + write$ + } + { + add.period$ " " * write$ + } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +FUNCTION { output.nonnull.dot.space } +{ % Stack in: ... R S T Stack out: ... R T File out: S + 's := + output.state mid.sentence = % { ". " * write$ } + { + ". " * write$ + } + { + output.state after.block = + { + add.period$ writeln "\newblock " write$ + } + { + output.state before.all = + { + write$ + } + { + add.period$ " " * write$ + } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +FUNCTION { output.nonnull.remove } +{ % Stack in: ... R S T Stack out: ... R T File out: S + 's := + output.state mid.sentence = + { + " " * write$ + } + { + output.state after.block = + { + add.period$ writeln "\newblock " write$ + } + { + output.state before.all = + { + write$ + } + { + add.period$ " " * write$ + } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +FUNCTION { output.nonnull.removenospace } +{ % Stack in: ... R S T Stack out: ... R T File out: S + 's := + output.state mid.sentence = + { + "" * write$ + } + { + output.state after.block = + { + add.period$ writeln "\newblock " write$ + } + { + output.state before.all = + { + write$ + } + { + add.period$ " " * write$ + } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +FUNCTION { output } +{ % discard top token if empty, else like output.nonnull + duplicate$ empty.or.unknown + 'pop$ + 'output.nonnull + if$ +} + +FUNCTION { output.dot.space } +{ % discard top token if empty, else like output.nonnull.dot.space + duplicate$ empty.or.unknown + 'pop$ + 'output.nonnull.dot.space + if$ +} + +FUNCTION { output.removenospace } +{ % discard top token if empty, else like output.nonnull.removenospace + duplicate$ empty.or.unknown + 'pop$ + 'output.nonnull.removenospace + if$ +} + +FUNCTION { output.check } +{ % like output, but warn if key name on top-of-stack is not set + 't := + duplicate$ empty.or.unknown + { pop$ "empty " t * " in " * cite$ * warning$ } + 'output.nonnull + if$ +} + +FUNCTION { bibinfo.output.check } +{ % like output.check, adding bibinfo field + 't := + duplicate$ empty.or.unknown + { pop$ "empty " t * " in " * cite$ * warning$ } + { "\bibinfo{" t "}{" * * swap$ * "}" * + output.nonnull } + if$ +} + +FUNCTION { output.check.dot.space } +{ % like output.dot.space, but warn if key name on top-of-stack is not set + 't := + duplicate$ empty.or.unknown + { pop$ "empty " t * " in " * cite$ * warning$ } + 'output.nonnull.dot.space + if$ +} + +FUNCTION { fin.block } +{ % functionally, but not logically, identical to fin.entry + add.period$ + writeln +} + +FUNCTION { fin.entry } +{ + add.period$ + writeln +} + +FUNCTION { new.sentence } +{ % update sentence state, with neither output nor stack change + output.state after.block = + 'skip$ + { + output.state before.all = + 'skip$ + { after.sentence 'output.state := } + if$ + } + if$ +} + +FUNCTION { fin.sentence } +{ + add.period$ + write$ + new.sentence + "" +} + +FUNCTION { new.block } +{ + output.state before.all = + 'skip$ + { after.block 'output.state := } + if$ +} + +FUNCTION { output.coden } % UTAH +{ % output non-empty CODEN as one-line sentence (stack untouched) + coden empty.or.unknown + { } + { "\showCODEN{" coden * "}" * writeln } + if$ +} + +% +% Sometimes articleno starts with the word 'Article' or 'Paper. +% (this is a bug of acmdl, sigh) +% We strip them. We assume eid or articleno is already on stack +% + +FUNCTION { strip.articleno.or.eid } +{ + 't := + t #1 #7 substring$ "Article" = + {t #8 t text.length$ substring$ 't :=} + { } + if$ + t #1 #7 substring$ "article" = + {t #8 t text.length$ substring$ 't :=} + { } + if$ + t #1 #5 substring$ "Paper" = + {t #6 t text.length$ substring$ 't :=} + { } + if$ + t #1 #5 substring$ "paper" = + {t #6 t text.length$ substring$ 't :=} + { } + if$ + % Strip any left trailing space or ~ + t #1 #1 substring$ " " = + {t #2 t text.length$ substring$ 't :=} + { } + if$ + t #1 #1 substring$ "~" = + {t #2 t text.length$ substring$ 't :=} + { } + if$ + t +} + + +FUNCTION { format.articleno } +{ + articleno empty.or.unknown not eid empty.or.unknown not and + { "Both articleno and eid are defined for " cite$ * warning$ } + 'skip$ + if$ + articleno empty.or.unknown eid empty.or.unknown and + { "" } + { + numpages empty.or.unknown + { "articleno or eid field, but no numpages field, in " + cite$ * warning$ } + { } + if$ + eid empty.or.unknown + { "Article \bibinfo{articleno}{" articleno strip.articleno.or.eid * "}" * } + { "Article \bibinfo{articleno}{" eid strip.articleno.or.eid * "}" * } + if$ + } + if$ +} + +FUNCTION { format.year } +{ % push year string or "[n.\,d.]" onto output stack + %% Because year is a mandatory field, we always force SOMETHING + %% to be output. If we do not know year but know date, we push date + %% as the last resort + "\bibinfo{year}{" + year empty.or.unknown + { date empty.or.unknown + { "[n.\,d.]" } + { date #1 #4 substring$ } + if$ + } + { year } + if$ + * "}" * +} + +FUNCTION { format.day.month } +{ % push "day month " or "month " or "" onto output stack + day empty.or.unknown + { + month empty.or.unknown + { "" } + { "\bibinfo{date}{" month * "} " *} + if$ + } + { + month empty.or.unknown + { "" } + { "\bibinfo{date}{" day * " " * month * "} " *} + if$ + } + if$ +} + +FUNCTION { format.day.month.year } % UTAH +{ % if month is empty, push "" else push "(MON.)" or "(DD MON.)" + % Needed for frequent periodicals: 2008. ... New York Times C-1, C-2, C-17 (23 Oct.) + % acm-*.bst addition: prefix parenthesized date string with + % ", Article nnn " + articleno empty.or.unknown eid empty.or.unknown and + { "" } + { output.state after.block = + {", " format.articleno * } + { format.articleno } + if$ + } + if$ + " (" * format.day.month * format.year * ")" * +} + +FUNCTION { output.day.month.year } % UTAH +{ % if month is empty value, do nothing; else output stack top and + % leave with new top string "(MON.)" or "(DD MON.)" + % Needed for frequent periodicals: 2008. ... New York Times C-1, C-2, C-17 (23 Oct.) + format.day.month.year + output.nonnull.remove +} + +FUNCTION { strip.doi } % UTAH +{ % Strip any Web address prefix to recover the bare DOI, leaving the + % result on the output stack, as recommended by CrossRef DOI + % documentation. + % For example, reduce "http://doi.acm.org/10.1145/1534530.1534545" to + % "10.1145/1534530.1534545". A suitable URL is later typeset and + % displayed as the LAST item in the reference list entry. Publisher Web + % sites wrap this with a suitable link to a real URL to resolve the DOI, + % and the master https://doi.org/ address is preferred, since publisher- + % specific URLs can disappear in response to economic events. All + % journals are encouraged by the DOI authorities to use that typeset + % format and link procedures for uniformity across all publications that + % include DOIs in reference lists. + % The numeric prefix is guaranteed to start with "10.", so we use + % that as a test. + % 2017-02-04 Added stripping of https:// (Boris) + doi #1 #3 substring$ "10." = + { doi } + { + doi 't := % get modifiable copy of DOI + + % Change https:// to http:// to strip both prefixes (BV) + + t #1 #8 substring$ "https://" = + { "http://" t #9 t text.length$ #8 - substring$ * 't := } + { } + if$ + + t #1 #7 substring$ "http://" = + { + t #8 t text.length$ #7 - substring$ 't := + + "INTERNAL STYLE-FILE ERROR" 's := + + % search for next "/" and assign its suffix to s + + { t text.length$ } + { + t #1 #1 substring$ "/" = + { + % save rest of string as true DOI (should be 10.xxxx/yyyy) + t #2 t text.length$ #1 - substring$ 's := + "" 't := % empty string t terminates the loop + } + { + % discard first character and continue loop: t <= substring(t,2,last) + t #2 t text.length$ #1 - substring$ 't := + } + if$ + } + while$ + + % check for valid DOI (should be 10.xxxx/yyyy) + s #1 #3 substring$ "10." = + { } + { "unrecognized DOI substring " s * " in DOI value [" * doi * "]" * warning$ } + if$ + + s % push the stripped DOI on the output stack + + } + { + "unrecognized DOI value [" doi * "]" * warning$ + doi % push the unrecognized original DOI on the output stack + } + if$ + } + if$ +} + +% +% Change by BV: added standard prefix to URL +% +FUNCTION { output.doi } % UTAH +{ % output non-empty DOI as one-line sentence (stack untouched) + doi empty.or.unknown + { } + { + "\href{https://doi.org/" strip.doi * "}{doi:\nolinkurl{" * + strip.doi * "}}" * writeln + } + if$ +} + +FUNCTION { output.isbn } % UTAH +{ % output non-empty ISBN-10 and/or ISBN-13 as one-line sentences (stack untouched) + show-isbn-10-and-13 + { + %% show both 10- and 13-digit ISBNs + isbn empty.or.unknown + { } + { + "\showISBNx{" isbn * "}" * writeln + } + if$ + isbn-13 empty.or.unknown + { } + { + "\showISBNxiii{" isbn-13 * "}" * writeln + } + if$ + } + { + %% show 10-digit ISBNs only if 13-digit ISBNs not available + isbn-13 empty.or.unknown + { + isbn empty.or.unknown + { } + { + "\showISBNx{" isbn * "}" * writeln + } + if$ + } + { + "\showISBNxiii{" isbn-13 * "}" * writeln + } + if$ + } + if$ +} + +FUNCTION { output.issn } % UTAH +{ % output non-empty ISSN as one-line sentence (stack untouched) + issn empty.or.unknown + { } + { "\showISSN{" issn * "}" * writeln } + if$ +} + +FUNCTION { output.issue } +{ % output non-empty issue number as a one-line sentence (stack untouched) + issue empty.or.unknown + { } + { "Issue " issue * "." * writeln } + if$ +} + +FUNCTION { output.lccn } % UTAH +{ % return with stack untouched + lccn empty.or.unknown + { } + { "\showLCCN{" lccn * "}" * writeln } + if$ +} + +FUNCTION { output.note } % UTAH +{ % return with stack empty + note empty.or.unknown + { } + { "\shownote{" note * "}" add.period$ * writeln } + if$ +} + +FUNCTION { output.note.check } % UTAH +{ % return with stack empty + note empty.or.unknown + { "empty note in " cite$ * warning$ } + { "\shownote{" note * "}" add.period$ * writeln } + if$ +} + +FUNCTION { output.eprint } % +{ % return with stack empty + eprint empty.or.unknown + { } + { "\showeprint" + archiveprefix empty.or.unknown + { eprinttype empty.or.unknown + { } + { "[" eprinttype "]" * * * } + if$ + } + { "[" archiveprefix "l" change.case$ "]" * * * } + if$ + "{" eprint "}" * * * + primaryclass empty.or.unknown + { eprintclass empty.or.unknown + { } + { "~[" eprintclass "]" * * * } + if$ + } + { "~[" primaryclass "]" * * * } + if$ + writeln + } + if$ +} + + +% +% Changes by BV 2011/04/15. Do not output +% url if doi is defined +% +% +% Changes by BV 2021/11/26. Output url even if doi is defined +% if distinctURL is not zero. +% +FUNCTION { output.url } % UTAH +{ % return with stack untouched + % output URL and associated lastaccessed fields + doi empty.or.unknown distinctURL empty.or.zero not or + { + url empty.or.unknown + { } + { new.sentence + %% Use \urldef, outside \showURL, so that %nn, #, etc in URLs work + %% correctly. Put the actual URL on its own line to reduce the + %% likelihood of BibTeX's nasty line wrapping after column 79. + %% \url{} can undo this, but if that doesn't work for some reason + %% the .bbl file would have to be repaired manually. + "\urldef\tempurl%" writeln + "\url{" url * "}" * writeln + + "\showURL{%" writeln + lastaccessed empty.or.unknown + { "" } + { "Retrieved " lastaccessed * " from " * } + if$ + "\tempurl" * + archived empty.or.unknown + { } + {"\urldef\tempurla%" writeln + "\url{" archived * "}%" * writeln + ", archived at [\tempurla]" * + } + if$ + "}" * writeln + } + if$ + } + { } + if$ +} + +FUNCTION { output.year.check } +{ % warn if year empty, output top string and leave " YEAR