| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| //===-- Single-precision e^x function -------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "exp_utils.h" | ||
| #include "math_utils.h" | ||
|
|
||
| #include "include/math.h" | ||
| #include "src/__support/common.h" | ||
|
|
||
| #include <stdint.h> | ||
|
|
||
| #define InvLn2N exp2f_data.invln2_scaled | ||
| #define T exp2f_data.tab | ||
| #define C exp2f_data.poly_scaled | ||
| #define SHIFT exp2f_data.shift | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| float LLVM_LIBC_ENTRYPOINT(expf)(float x) { | ||
| uint32_t abstop; | ||
| uint64_t ki, t; | ||
| // double_t for better performance on targets with FLT_EVAL_METHOD == 2. | ||
| double_t kd, xd, z, r, r2, y, s; | ||
|
|
||
| xd = static_cast<double_t>(x); | ||
| abstop = top12_bits(x) & 0x7ff; | ||
| if (unlikely(abstop >= top12_bits(88.0f))) { | ||
| // |x| >= 88 or x is nan. | ||
| if (as_uint32_bits(x) == as_uint32_bits(-INFINITY)) | ||
| return 0.0f; | ||
| if (abstop >= top12_bits(INFINITY)) | ||
| return x + x; | ||
| if (x > as_float(0x42b17217)) // x > log(0x1p128) ~= 88.72 | ||
| return overflow<float>(0); | ||
| if (x < as_float(0xc2cff1b4)) // x < log(0x1p-150) ~= -103.97 | ||
| return underflow<float>(0); | ||
| if (x < as_float(0xc2ce8ecf)) // x < log(0x1p-149) ~= -103.28 | ||
| return may_underflow<float>(0); | ||
| } | ||
|
|
||
| // x*N/Ln2 = k + r with r in [-1/2, 1/2] and int k. | ||
| z = InvLn2N * xd; | ||
|
|
||
| // Round and convert z to int, the result is in [-150*N, 128*N] and | ||
| // ideally nearest int is used, otherwise the magnitude of r can be | ||
| // bigger which gives larger approximation error. | ||
| kd = static_cast<double>(z + SHIFT); | ||
| ki = as_uint64_bits(kd); | ||
| kd -= SHIFT; | ||
| r = z - kd; | ||
|
|
||
| // exp(x) = 2^(k/N) * 2^(r/N) ~= s *(C0*r^3 + C1*r^2 + C2*r + 1) | ||
| t = T[ki % N]; | ||
| t += ki << (52 - EXP2F_TABLE_BITS); | ||
| s = as_double(t); | ||
| z = C[0] * r + C[1]; | ||
| r2 = r * r; | ||
| y = C[2] * r + 1; | ||
| y = z * r2 + y; | ||
| y = y * s; | ||
| return static_cast<float>(y); | ||
| } | ||
|
|
||
| } // namespace __llvm_libc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| //===-- Implementation header for expf --------------------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIBC_SRC_MATH_EXPF_H | ||
| #define LLVM_LIBC_SRC_MATH_EXPF_H | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| float expf(float x); | ||
|
|
||
| } // namespace __llvm_libc | ||
|
|
||
| #endif // LLVM_LIBC_SRC_MATH_EXPF_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| //===-- Implementation of math utils --------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "math_utils.h" | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| const float XFlowValues<float>::overflow_value = | ||
| as_float(0x70000000); // 0x1p97f | ||
| const float XFlowValues<float>::underflow_value = | ||
| as_float(0x10000000); // 0x1p97f | ||
| const float XFlowValues<float>::may_underflow_value = | ||
| as_float(0x1a200000); // 0x1.4p-75f | ||
|
|
||
| const double XFlowValues<double>::overflow_value = | ||
| as_double(0x7000000000000000); // 0x1p769 | ||
| const double XFlowValues<double>::underflow_value = | ||
| as_double(0x1000000000000000); // 0x1p-767 | ||
| const double XFlowValues<double>::may_underflow_value = | ||
| as_double(0x1e58000000000000); // 0x1.8p-538 | ||
|
|
||
| } // namespace __llvm_libc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| //===-- Unittests for exp2f -----------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "include/errno.h" | ||
| #include "include/math.h" | ||
| #include "src/errno/llvmlibc_errno.h" | ||
| #include "src/math/exp2f.h" | ||
| #include "utils/FPUtil/BitPatterns.h" | ||
| #include "utils/FPUtil/FloatOperations.h" | ||
| #include "utils/FPUtil/FloatProperties.h" | ||
| #include "utils/MPFRWrapper/MPFRUtils.h" | ||
| #include "utils/UnitTest/Test.h" | ||
|
|
||
| #include <stdint.h> | ||
|
|
||
| using __llvm_libc::fputil::isNegativeQuietNaN; | ||
| using __llvm_libc::fputil::isQuietNaN; | ||
| using __llvm_libc::fputil::valueAsBits; | ||
| using __llvm_libc::fputil::valueFromBits; | ||
|
|
||
| using BitPatterns = __llvm_libc::fputil::BitPatterns<float>; | ||
|
|
||
| namespace mpfr = __llvm_libc::testing::mpfr; | ||
|
|
||
| // 12 additional bits of precision over the base precision of a |float| | ||
| // value. | ||
| static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12, | ||
| 0xFFF}; | ||
|
|
||
| TEST(exp2fTest, SpecialNumbers) { | ||
| llvmlibc_errno = 0; | ||
|
|
||
| EXPECT_TRUE( | ||
| isQuietNaN(__llvm_libc::exp2f(valueFromBits(BitPatterns::aQuietNaN)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_TRUE(isNegativeQuietNaN( | ||
| __llvm_libc::exp2f(valueFromBits(BitPatterns::aNegativeQuietNaN)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_TRUE(isQuietNaN( | ||
| __llvm_libc::exp2f(valueFromBits(BitPatterns::aSignallingNaN)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_TRUE(isNegativeQuietNaN( | ||
| __llvm_libc::exp2f(valueFromBits(BitPatterns::aNegativeSignallingNaN)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_EQ(BitPatterns::inf, | ||
| valueAsBits(__llvm_libc::exp2f(valueFromBits(BitPatterns::inf)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_EQ(BitPatterns::zero, valueAsBits(__llvm_libc::exp2f( | ||
| valueFromBits(BitPatterns::negInf)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_EQ(BitPatterns::one, | ||
| valueAsBits(__llvm_libc::exp2f(valueFromBits(BitPatterns::zero)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_EQ(BitPatterns::one, valueAsBits(__llvm_libc::exp2f( | ||
| valueFromBits(BitPatterns::negZero)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
| } | ||
|
|
||
| TEST(ExpfTest, Overflow) { | ||
| llvmlibc_errno = 0; | ||
| EXPECT_EQ(BitPatterns::inf, | ||
| valueAsBits(__llvm_libc::exp2f(valueFromBits(0x7f7fffffU)))); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
|
|
||
| llvmlibc_errno = 0; | ||
| EXPECT_EQ(BitPatterns::inf, | ||
| valueAsBits(__llvm_libc::exp2f(valueFromBits(0x43000000U)))); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
|
|
||
| llvmlibc_errno = 0; | ||
| EXPECT_EQ(BitPatterns::inf, | ||
| valueAsBits(__llvm_libc::exp2f(valueFromBits(0x43000001U)))); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
| } | ||
|
|
||
| // Test with inputs which are the borders of underflow/overflow but still | ||
| // produce valid results without setting errno. | ||
| TEST(ExpfTest, Borderline) { | ||
| float x; | ||
|
|
||
| llvmlibc_errno = 0; | ||
| x = valueFromBits(0x42fa0001U); | ||
| EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| x = valueFromBits(0x42ffffffU); | ||
| EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| x = valueFromBits(0xc2fa0001U); | ||
| EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| x = valueFromBits(0xc2fc0000U); | ||
| EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| x = valueFromBits(0xc2fc0001U); | ||
| EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| x = valueFromBits(0xc3150000U); | ||
| EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
| } | ||
|
|
||
| TEST(ExpfTest, Underflow) { | ||
| llvmlibc_errno = 0; | ||
| EXPECT_EQ(BitPatterns::zero, | ||
| valueAsBits(__llvm_libc::exp2f(valueFromBits(0xff7fffffU)))); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
|
|
||
| llvmlibc_errno = 0; | ||
| float x = valueFromBits(0xc3158000U); | ||
| EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
|
|
||
| llvmlibc_errno = 0; | ||
| x = valueFromBits(0xc3165432U); | ||
| EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
| } | ||
|
|
||
| TEST(exp2fTest, InFloatRange) { | ||
| constexpr uint32_t count = 1000000; | ||
| constexpr uint32_t step = UINT32_MAX / count; | ||
| for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) { | ||
| float x = valueFromBits(v); | ||
| if (isnan(x) || isinf(x)) | ||
| continue; | ||
| llvmlibc_errno = 0; | ||
| float result = __llvm_libc::exp2f(x); | ||
|
|
||
| // If the computation resulted in an error or did not produce valid result | ||
| // in the single-precision floating point range, then ignore comparing with | ||
| // MPFR result as MPFR can still produce valid results because of its | ||
| // wider precision. | ||
| if (isnan(result) || isinf(result) || llvmlibc_errno != 0) | ||
| continue; | ||
| ASSERT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| //===-- Unittests for expf ------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "include/errno.h" | ||
| #include "include/math.h" | ||
| #include "src/errno/llvmlibc_errno.h" | ||
| #include "src/math/expf.h" | ||
| #include "utils/FPUtil/BitPatterns.h" | ||
| #include "utils/FPUtil/FloatOperations.h" | ||
| #include "utils/FPUtil/FloatProperties.h" | ||
| #include "utils/MPFRWrapper/MPFRUtils.h" | ||
| #include "utils/UnitTest/Test.h" | ||
|
|
||
| #include <stdint.h> | ||
|
|
||
| using __llvm_libc::fputil::isNegativeQuietNaN; | ||
| using __llvm_libc::fputil::isQuietNaN; | ||
| using __llvm_libc::fputil::valueAsBits; | ||
| using __llvm_libc::fputil::valueFromBits; | ||
|
|
||
| using BitPatterns = __llvm_libc::fputil::BitPatterns<float>; | ||
|
|
||
| namespace mpfr = __llvm_libc::testing::mpfr; | ||
|
|
||
| // 12 additional bits of precision over the base precision of a |float| | ||
| // value. | ||
| static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12, | ||
| 0xFFF}; | ||
|
|
||
| TEST(ExpfTest, SpecialNumbers) { | ||
| llvmlibc_errno = 0; | ||
|
|
||
| EXPECT_TRUE( | ||
| isQuietNaN(__llvm_libc::expf(valueFromBits(BitPatterns::aQuietNaN)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_TRUE(isNegativeQuietNaN( | ||
| __llvm_libc::expf(valueFromBits(BitPatterns::aNegativeQuietNaN)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_TRUE(isQuietNaN( | ||
| __llvm_libc::expf(valueFromBits(BitPatterns::aSignallingNaN)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_TRUE(isNegativeQuietNaN( | ||
| __llvm_libc::expf(valueFromBits(BitPatterns::aNegativeSignallingNaN)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_EQ(BitPatterns::inf, | ||
| valueAsBits(__llvm_libc::expf(valueFromBits(BitPatterns::inf)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_EQ(BitPatterns::zero, | ||
| valueAsBits(__llvm_libc::expf(valueFromBits(BitPatterns::negInf)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_EQ(BitPatterns::one, | ||
| valueAsBits(__llvm_libc::expf(valueFromBits(BitPatterns::zero)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| EXPECT_EQ(BitPatterns::one, valueAsBits(__llvm_libc::expf( | ||
| valueFromBits(BitPatterns::negZero)))); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
| } | ||
|
|
||
| TEST(ExpfTest, Overflow) { | ||
| llvmlibc_errno = 0; | ||
| EXPECT_EQ(BitPatterns::inf, | ||
| valueAsBits(__llvm_libc::expf(valueFromBits(0x7f7fffffU)))); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
|
|
||
| llvmlibc_errno = 0; | ||
| EXPECT_EQ(BitPatterns::inf, | ||
| valueAsBits(__llvm_libc::expf(valueFromBits(0x42cffff8U)))); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
|
|
||
| llvmlibc_errno = 0; | ||
| EXPECT_EQ(BitPatterns::inf, | ||
| valueAsBits(__llvm_libc::expf(valueFromBits(0x42d00008U)))); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
| } | ||
|
|
||
| TEST(ExpfTest, Underflow) { | ||
| llvmlibc_errno = 0; | ||
| EXPECT_EQ(BitPatterns::zero, | ||
| valueAsBits(__llvm_libc::expf(valueFromBits(0xff7fffffU)))); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
|
|
||
| llvmlibc_errno = 0; | ||
| EXPECT_EQ(BitPatterns::zero, | ||
| valueAsBits(__llvm_libc::expf(valueFromBits(0xc2cffff8U)))); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
|
|
||
| llvmlibc_errno = 0; | ||
| EXPECT_EQ(BitPatterns::zero, | ||
| valueAsBits(__llvm_libc::expf(valueFromBits(0xc2d00008U)))); | ||
| EXPECT_EQ(llvmlibc_errno, ERANGE); | ||
| } | ||
|
|
||
| // Test with inputs which are the borders of underflow/overflow but still | ||
| // produce valid results without setting errno. | ||
| TEST(ExpfTest, Borderline) { | ||
| float x; | ||
|
|
||
| llvmlibc_errno = 0; | ||
| x = valueFromBits(0x42affff8U); | ||
| ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| x = valueFromBits(0x42b00008U); | ||
| ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| x = valueFromBits(0xc2affff8U); | ||
| ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
|
|
||
| x = valueFromBits(0xc2b00008U); | ||
| ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance); | ||
| EXPECT_EQ(llvmlibc_errno, 0); | ||
| } | ||
|
|
||
| TEST(ExpfTest, InFloatRange) { | ||
| constexpr uint32_t count = 1000000; | ||
| constexpr uint32_t step = UINT32_MAX / count; | ||
| for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) { | ||
| float x = valueFromBits(v); | ||
| if (isnan(x) || isinf(x)) | ||
| continue; | ||
| llvmlibc_errno = 0; | ||
| float result = __llvm_libc::expf(x); | ||
|
|
||
| // If the computation resulted in an error or did not produce valid result | ||
| // in the single-precision floating point range, then ignore comparing with | ||
| // MPFR result as MPFR can still produce valid results because of its | ||
| // wider precision. | ||
| if (isnan(result) || isinf(result) || llvmlibc_errno != 0) | ||
| continue; | ||
| ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance); | ||
| } | ||
| } |