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 @@ -3,10 +3,13 @@ HDRS FEnvImpl.h DEPENDS + libc.include.errno libc.include.fenv + libc.include.math libc.src.__support.macros.attributes libc.src.__support.macros.properties.architectures libc.src.__support.macros.sanitizer + libc.src.errno.errno ) add_header_library( diff --git a/libc/src/__support/FPUtil/FEnvImpl.h b/libc/src/__support/FPUtil/FEnvImpl.h --- a/libc/src/__support/FPUtil/FEnvImpl.h +++ b/libc/src/__support/FPUtil/FEnvImpl.h @@ -12,6 +12,10 @@ #include "src/__support/macros/attributes.h" // LIBC_INLINE #include "src/__support/macros/properties/architectures.h" +#include +#include +#include + #if defined(LIBC_TARGET_ARCH_IS_AARCH64) #if defined(__APPLE__) #include "aarch64/fenv_darwin_impl.h" @@ -21,10 +25,8 @@ #elif defined(LIBC_TARGET_ARCH_IS_X86) #include "x86_64/FEnvImpl.h" #else -#include -namespace __llvm_libc { -namespace fputil { +namespace __llvm_libc::fputil { // All dummy functions silently succeed. @@ -44,8 +46,28 @@ LIBC_INLINE int set_env(const fenv_t *) { return 0; } -} // namespace fputil -} // namespace __llvm_libc +} // namespace __llvm_libc::fputil #endif +namespace __llvm_libc::fputil { + +LIBC_INLINE int set_except_if_required(int excepts) { + if (math_errhandling & MATH_ERREXCEPT) + return set_except(excepts); + return 0; +} + +LIBC_INLINE int raise_except_if_required(int excepts) { + if (math_errhandling & MATH_ERREXCEPT) + return raise_except(excepts); + return 0; +} + +LIBC_INLINE void set_errno_if_required(int err) { + if (math_errhandling & MATH_ERRNO) + errno = err; +} + +} // namespace __llvm_libc::fputil + #endif // LLVM_LIBC_SRC_SUPPORT_FPUTIL_FENVIMPL_H diff --git a/libc/src/math/generic/acosf.cpp b/libc/src/math/generic/acosf.cpp --- a/libc/src/math/generic/acosf.cpp +++ b/libc/src/math/generic/acosf.cpp @@ -76,8 +76,8 @@ // |x| > 1, return NaNs. if (LIBC_UNLIKELY(x_abs > 0x3f80'0000U)) { if (x_abs <= 0x7f80'0000U) { - errno = EDOM; - fputil::set_except(FE_INVALID); + fputil::set_errno_if_required(EDOM); + fputil::raise_except_if_required(FE_INVALID); } return x + FPBits::build_nan(1 << (fputil::MantissaWidth::VALUE - 1)); diff --git a/libc/src/math/generic/asinf.cpp b/libc/src/math/generic/asinf.cpp --- a/libc/src/math/generic/asinf.cpp +++ b/libc/src/math/generic/asinf.cpp @@ -105,8 +105,8 @@ // |x| > 1, return NaNs. if (LIBC_UNLIKELY(x_abs > 0x3f80'0000U)) { if (x_abs <= 0x7f80'0000U) { - errno = EDOM; - fputil::set_except(FE_INVALID); + fputil::set_errno_if_required(EDOM); + fputil::raise_except_if_required(FE_INVALID); } return x + FPBits::build_nan(1 << (fputil::MantissaWidth::VALUE - 1)); 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 @@ -114,8 +114,8 @@ // x is inf or nan. if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) { if (x_abs == 0x7f80'0000U) { - errno = EDOM; - fputil::set_except(FE_INVALID); + fputil::set_errno_if_required(EDOM); + fputil::raise_except_if_required(FE_INVALID); } return x + FPBits::build_nan(1 << (fputil::MantissaWidth::VALUE - 1)); diff --git a/libc/src/math/generic/coshf.cpp b/libc/src/math/generic/coshf.cpp --- a/libc/src/math/generic/coshf.cpp +++ b/libc/src/math/generic/coshf.cpp @@ -36,7 +36,8 @@ if (LIBC_UNLIKELY(rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)) return FPBits(FPBits::MAX_NORMAL).get_val(); - errno = ERANGE; + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_OVERFLOW); return x + FPBits::inf().get_val(); } diff --git a/libc/src/math/generic/exp10f.cpp b/libc/src/math/generic/exp10f.cpp --- a/libc/src/math/generic/exp10f.cpp +++ b/libc/src/math/generic/exp10f.cpp @@ -40,7 +40,8 @@ return x; if (fputil::get_round() == FE_UPWARD) return static_cast(FPBits(FPBits::MIN_SUBNORMAL)); - errno = ERANGE; + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_UNDERFLOW); return 0.0f; } // x >= log10(2^128) or nan @@ -51,7 +52,8 @@ if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) return static_cast(FPBits(FPBits::MAX_NORMAL)); - errno = ERANGE; + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_OVERFLOW); } // x is +inf or nan return x + static_cast(FPBits::inf()); diff --git a/libc/src/math/generic/exp2f.cpp b/libc/src/math/generic/exp2f.cpp --- a/libc/src/math/generic/exp2f.cpp +++ b/libc/src/math/generic/exp2f.cpp @@ -48,7 +48,8 @@ if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) return static_cast(FPBits(FPBits::MAX_NORMAL)); - errno = ERANGE; + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_OVERFLOW); } // x is +inf or nan return x + FPBits::inf().get_val(); @@ -63,8 +64,10 @@ return x; if (fputil::get_round() == FE_UPWARD) return FPBits(FPBits::MIN_SUBNORMAL).get_val(); - if (x != 0.0f) - errno = ERANGE; + if (x != 0.0f) { + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_UNDERFLOW); + } return 0.0f; } } diff --git a/libc/src/math/generic/expf.cpp b/libc/src/math/generic/expf.cpp --- a/libc/src/math/generic/expf.cpp +++ b/libc/src/math/generic/expf.cpp @@ -50,7 +50,8 @@ return x; if (fputil::get_round() == FE_UPWARD) return static_cast(FPBits(FPBits::MIN_SUBNORMAL)); - errno = ERANGE; + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_UNDERFLOW); return 0.0f; } // x >= 89 or nan @@ -61,7 +62,8 @@ if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) return static_cast(FPBits(FPBits::MAX_NORMAL)); - errno = ERANGE; + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_OVERFLOW); } // x is +inf or nan return x + static_cast(FPBits::inf()); diff --git a/libc/src/math/generic/expm1f.cpp b/libc/src/math/generic/expm1f.cpp --- a/libc/src/math/generic/expm1f.cpp +++ b/libc/src/math/generic/expm1f.cpp @@ -69,7 +69,8 @@ if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO) return static_cast(FPBits(FPBits::MAX_NORMAL)); - errno = ERANGE; + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_OVERFLOW); } return x + static_cast(FPBits::inf()); } diff --git a/libc/src/math/generic/sincosf.cpp b/libc/src/math/generic/sincosf.cpp --- a/libc/src/math/generic/sincosf.cpp +++ b/libc/src/math/generic/sincosf.cpp @@ -144,8 +144,8 @@ // x is inf or nan. if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) { if (x_abs == 0x7f80'0000U) { - errno = EDOM; - fputil::set_except(FE_INVALID); + fputil::set_errno_if_required(EDOM); + fputil::raise_except_if_required(FE_INVALID); } *sinp = x + FPBits::build_nan(1 << (fputil::MantissaWidth::VALUE - 1)); diff --git a/libc/src/math/generic/sinf.cpp b/libc/src/math/generic/sinf.cpp --- a/libc/src/math/generic/sinf.cpp +++ b/libc/src/math/generic/sinf.cpp @@ -135,8 +135,8 @@ if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) { if (x_abs == 0x7f80'0000U) { - errno = EDOM; - fputil::set_except(FE_INVALID); + fputil::set_errno_if_required(EDOM); + fputil::raise_except_if_required(FE_INVALID); } return x + FPBits::build_nan(1 << (fputil::MantissaWidth::VALUE - 1)); diff --git a/libc/src/math/generic/sinhf.cpp b/libc/src/math/generic/sinhf.cpp --- a/libc/src/math/generic/sinhf.cpp +++ b/libc/src/math/generic/sinhf.cpp @@ -42,7 +42,8 @@ return FPBits(FPBits::MAX_NORMAL).get_val(); } - errno = ERANGE; + fputil::set_errno_if_required(ERANGE); + fputil::raise_except_if_required(FE_OVERFLOW); return x + FPBits::inf(sign).get_val(); } 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 @@ -111,8 +111,8 @@ // Inf or NaN if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) { if (x_abs == 0x7f80'0000U) { - errno = EDOM; - fputil::set_except(FE_INVALID); + fputil::set_errno_if_required(EDOM); + fputil::raise_except_if_required(FE_INVALID); } return x + FPBits::build_nan(1 << (fputil::MantissaWidth::VALUE - 1)); diff --git a/libc/test/src/math/coshf_test.cpp b/libc/test/src/math/coshf_test.cpp --- a/libc/test/src/math/coshf_test.cpp +++ b/libc/test/src/math/coshf_test.cpp @@ -44,13 +44,16 @@ TEST(LlvmLibcCoshfTest, Overflow) { errno = 0; - EXPECT_FP_EQ(inf, __llvm_libc::coshf(float(FPBits(0x7f7fffffU)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::coshf(float(FPBits(0x7f7fffffU))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::coshf(float(FPBits(0x42cffff8U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::coshf(float(FPBits(0x42cffff8U))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::coshf(float(FPBits(0x42d00008U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::coshf(float(FPBits(0x42d00008U))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); } diff --git a/libc/test/src/math/exp10f_test.cpp b/libc/test/src/math/exp10f_test.cpp --- a/libc/test/src/math/exp10f_test.cpp +++ b/libc/test/src/math/exp10f_test.cpp @@ -41,13 +41,33 @@ TEST(LlvmLibcExp10fTest, Overflow) { errno = 0; - EXPECT_FP_EQ(inf, __llvm_libc::exp10f(float(FPBits(0x7f7fffffU)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::exp10f(float(FPBits(0x7f7fffffU))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::exp10f(float(FPBits(0x43000000U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::exp10f(float(FPBits(0x43000000U))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::exp10f(float(FPBits(0x43000001U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::exp10f(float(FPBits(0x43000001U))), FE_OVERFLOW); + EXPECT_MATH_ERRNO(ERANGE); +} + +TEST(LlvmLibcExp10fTest, Underflow) { + errno = 0; + EXPECT_FP_EQ_WITH_EXCEPTION( + 0.0f, __llvm_libc::exp10f(float(FPBits(0xff7fffffU))), FE_UNDERFLOW); + EXPECT_MATH_ERRNO(ERANGE); + + float x = float(FPBits(0xc2cffff8U)); + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, x, + __llvm_libc::exp10f(x), 0.5); + EXPECT_MATH_ERRNO(ERANGE); + + x = float(FPBits(0xc2d00008U)); + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Exp10, x, + __llvm_libc::exp10f(x), 0.5); EXPECT_MATH_ERRNO(ERANGE); } diff --git a/libc/test/src/math/exp2f_test.cpp b/libc/test/src/math/exp2f_test.cpp --- a/libc/test/src/math/exp2f_test.cpp +++ b/libc/test/src/math/exp2f_test.cpp @@ -42,13 +42,16 @@ TEST(LlvmLibcExp2fTest, Overflow) { errno = 0; - EXPECT_FP_EQ(inf, __llvm_libc::exp2f(float(FPBits(0x7f7fffffU)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::exp2f(float(FPBits(0x7f7fffffU))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::exp2f(float(FPBits(0x43000000U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::exp2f(float(FPBits(0x43000000U))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::exp2f(float(FPBits(0x43000001U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::exp2f(float(FPBits(0x43000001U))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); } @@ -79,7 +82,8 @@ TEST(LlvmLibcExp2fTest, Underflow) { errno = 0; - EXPECT_FP_EQ(0.0f, __llvm_libc::exp2f(float(FPBits(0xff7fffffU)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + 0.0f, __llvm_libc::exp2f(float(FPBits(0xff7fffffU))), FE_UNDERFLOW); EXPECT_MATH_ERRNO(ERANGE); float x = float(FPBits(0xc3158000U)); diff --git a/libc/test/src/math/expf_test.cpp b/libc/test/src/math/expf_test.cpp --- a/libc/test/src/math/expf_test.cpp +++ b/libc/test/src/math/expf_test.cpp @@ -41,19 +41,23 @@ TEST(LlvmLibcExpfTest, Overflow) { errno = 0; - EXPECT_FP_EQ(inf, __llvm_libc::expf(float(FPBits(0x7f7fffffU)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::expf(float(FPBits(0x7f7fffffU))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::expf(float(FPBits(0x42cffff8U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::expf(float(FPBits(0x42cffff8U))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::expf(float(FPBits(0x42d00008U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::expf(float(FPBits(0x42d00008U))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); } TEST(LlvmLibcExpfTest, Underflow) { errno = 0; - EXPECT_FP_EQ(0.0f, __llvm_libc::expf(float(FPBits(0xff7fffffU)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + 0.0f, __llvm_libc::expf(float(FPBits(0xff7fffffU))), FE_UNDERFLOW); EXPECT_MATH_ERRNO(ERANGE); float x = float(FPBits(0xc2cffff8U)); diff --git a/libc/test/src/math/expm1f_test.cpp b/libc/test/src/math/expm1f_test.cpp --- a/libc/test/src/math/expm1f_test.cpp +++ b/libc/test/src/math/expm1f_test.cpp @@ -41,13 +41,16 @@ TEST(LlvmLibcExpm1fTest, Overflow) { errno = 0; - EXPECT_FP_EQ(inf, __llvm_libc::expm1f(float(FPBits(0x7f7fffffU)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::expm1f(float(FPBits(0x7f7fffffU))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::expm1f(float(FPBits(0x42cffff8U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::expm1f(float(FPBits(0x42cffff8U))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::expm1f(float(FPBits(0x42d00008U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::expm1f(float(FPBits(0x42d00008U))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); } diff --git a/libc/test/src/math/sinhf_test.cpp b/libc/test/src/math/sinhf_test.cpp --- a/libc/test/src/math/sinhf_test.cpp +++ b/libc/test/src/math/sinhf_test.cpp @@ -68,13 +68,16 @@ TEST(LlvmLibcSinhfTest, Overflow) { errno = 0; - EXPECT_FP_EQ(inf, __llvm_libc::sinhf(float(FPBits(0x7f7fffffU)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::sinhf(float(FPBits(0x7f7fffffU))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::sinhf(float(FPBits(0x42cffff8U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::sinhf(float(FPBits(0x42cffff8U))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); - EXPECT_FP_EQ(inf, __llvm_libc::sinhf(float(FPBits(0x42d00008U)))); + EXPECT_FP_EQ_WITH_EXCEPTION( + inf, __llvm_libc::sinhf(float(FPBits(0x42d00008U))), FE_OVERFLOW); EXPECT_MATH_ERRNO(ERANGE); }