diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt --- a/libc/src/__support/FPUtil/CMakeLists.txt +++ b/libc/src/__support/FPUtil/CMakeLists.txt @@ -102,6 +102,7 @@ DEPENDS .fp_bits .fenv_impl + libc.src.__support.CPP.optional ) diff --git a/libc/src/__support/FPUtil/except_value_utils.h b/libc/src/__support/FPUtil/except_value_utils.h --- a/libc/src/__support/FPUtil/except_value_utils.h +++ b/libc/src/__support/FPUtil/except_value_utils.h @@ -11,57 +11,97 @@ #include "FEnvImpl.h" #include "FPBits.h" +#include "src/__support/CPP/optional.h" namespace __llvm_libc { namespace fputil { -template struct ExceptionalValues { - using UIntType = typename FPBits::UIntType; +// This file contains utility functions and classes to manage exceptional values +// when there are many of them. +// +// Example usage: +// +// Define list of exceptional inputs and outputs: +// static constexpr int N = ...; // Number of exceptional values. +// static constexpr fputil::ExceptValues Excepts { +// { }, +// { } +// }; +// +// Check for exceptional inputs: +// if (auto result = check_except_values(Excepts, x_bits); +// unlikely(result.has_value())) +// return result.value(); + +template struct ExceptValueOutput { + T rnd_towardzero_result; + T rnd_upward_offset; + T rnd_downward_offset; + T rnd_tonearest_offset; +}; + +template struct ExceptValues { static constexpr int SIZE = N; // Input bits. - UIntType inputs[SIZE]; - // Output bits contains 4 values: - // output[i][0]: output bits corresponding to FE_TOWARDZERO - // output[i][1]: offset for FE_UPWARD - // output[i][2]: offset for FE_DOWNWARD - // output[i][3]: offset for FE_TONEAREST - UIntType outputs[SIZE][4]; + T inputs[SIZE]; + // Output bits. + ExceptValueOutput outputs[SIZE]; }; -template struct ExceptionChecker { - using UIntType = typename FPBits::UIntType; - using FPBits = FPBits; - using ExceptionalValues = ExceptionalValues; - - static bool check_odd_func(const ExceptionalValues &ExceptVals, - UIntType x_abs, bool sign, T &result) { - for (int i = 0; i < N; ++i) { - if (unlikely(x_abs == ExceptVals.inputs[i])) { - UIntType out_bits = ExceptVals.outputs[i][0]; // FE_TOWARDZERO - switch (fputil::get_round()) { - case FE_UPWARD: - out_bits += - sign ? ExceptVals.outputs[i][2] : ExceptVals.outputs[i][1]; - break; - case FE_DOWNWARD: - out_bits += - sign ? ExceptVals.outputs[i][1] : ExceptVals.outputs[i][2]; - break; - case FE_TONEAREST: - out_bits += ExceptVals.outputs[i][3]; - break; - } - result = FPBits(out_bits).get_val(); - if (sign) - result = -result; +template ::UIntType> +cpp::optional check_except_values(const ExceptValues &ExceptVals, + BitType x_bits) { + for (int i = 0; i < N; ++i) { + if (unlikely(x_bits == ExceptVals.inputs[i])) { + BitType out_bits = ExceptVals.outputs[i].rnd_towardzero_result; + switch (fputil::get_round()) { + case FE_UPWARD: + out_bits += ExceptVals.outputs[i].rnd_upward_offset; + break; + case FE_DOWNWARD: + out_bits += ExceptVals.outputs[i].rnd_downward_offset; + break; + case FE_TONEAREST: + out_bits += ExceptVals.outputs[i].rnd_tonearest_offset; + break; + } + return FPBits(out_bits).get_val(); + } + } + return cpp::nullopt; +} - return true; +// Check exceptional values for odd functions: f(-x) = -f(x). +template ::UIntType> +cpp::optional +check_odd_func_except_values(const ExceptValues &ExceptVals, + BitType x_abs, bool sign) { + for (int i = 0; i < N; ++i) { + if (unlikely(x_abs == ExceptVals.inputs[i])) { + BitType out_bits = ExceptVals.outputs[i].rnd_towardzero_result; + switch (fputil::get_round()) { + case FE_UPWARD: + out_bits += sign ? ExceptVals.outputs[i].rnd_downward_offset + : ExceptVals.outputs[i].rnd_upward_offset; + break; + case FE_DOWNWARD: + out_bits += sign ? ExceptVals.outputs[i].rnd_upward_offset + : ExceptVals.outputs[i].rnd_downward_offset; + break; + case FE_TONEAREST: + out_bits += ExceptVals.outputs[i].rnd_tonearest_offset; + break; } + T result = FPBits(out_bits).get_val(); + if (sign) + result = -result; + + return result; } - return false; } -}; + return cpp::nullopt; +} } // namespace fputil diff --git a/libc/src/math/generic/cosf.cpp b/libc/src/math/generic/cosf.cpp --- a/libc/src/math/generic/cosf.cpp +++ b/libc/src/math/generic/cosf.cpp @@ -20,9 +20,9 @@ namespace __llvm_libc { // Exceptional cases for cosf. -static constexpr int COSF_EXCEPTS = 6; +static constexpr int N_EXCEPTS = 6; -static constexpr fputil::ExceptionalValues CosfExcepts{ +static constexpr fputil::ExceptValues COSF_EXCEPTS{ /* inputs */ { 0x55325019, // x = 0x1.64a032p43 0x5922aa80, // x = 0x1.4555p51 @@ -110,12 +110,10 @@ #endif // LIBC_TARGET_HAS_FMA } - using ExceptChecker = typename fputil::ExceptionChecker; - { - float result; - if (ExceptChecker::check_odd_func(CosfExcepts, x_abs, false, result)) - return result; - } + if (auto r = + fputil::check_except_values(COSF_EXCEPTS, x_abs); + unlikely(r.has_value())) + return r.value(); // x is inf or nan. if (unlikely(x_abs >= 0x7f80'0000U)) { diff --git a/libc/src/math/generic/tanf.cpp b/libc/src/math/generic/tanf.cpp --- a/libc/src/math/generic/tanf.cpp +++ b/libc/src/math/generic/tanf.cpp @@ -56,9 +56,9 @@ }; // Exceptional cases for tanf. -static constexpr int TANF_EXCEPTS = 6; +static constexpr int N_EXCEPTS = 6; -static constexpr fputil::ExceptionalValues TanfExcepts{ +static constexpr fputil::ExceptValues TANF_EXCEPTS{ /* inputs */ { 0x531d744c, // x = 0x1.3ae898p39 0x57d7b0ed, // x = 0x1.af61dap48 @@ -200,14 +200,10 @@ k = small_range_reduction(xd, y); } else { - using ExceptChecker = - typename fputil::ExceptionChecker; - { - float result; - if (ExceptChecker::check_odd_func(TanfExcepts, x_abs, x_sign <= 0.0, - result)) - return result; - } + if (auto r = fputil::check_odd_func_except_values( + TANF_EXCEPTS, x_abs, x_sign <= 0.0); + unlikely(r.has_value())) + return r.value(); fputil::FPBits x_bits(x_abs); k = large_range_reduction(xd, x_bits.get_exponent(), y);