| //===----------------------------------------------------------------------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// Smoke tests for powf. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "hdr/fenv_macros.h" | |
| #include "hdr/math_macros.h" | |
| #include "hdr/stdint_proxy.h" | |
| #include "src/__support/FPUtil/FPBits.h" | |
| #include "src/__support/math/powf_double_eval.h" | |
| #include "src/__support/math/powf_float_eval.h" | |
| #include "src/math/powf.h" | |
| #include "test/UnitTest/FPMatcher.h" | |
| #include "test/UnitTest/Test.h" | |
| #ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS | |
| #define TOLERANCE 1 | |
| #else | |
| #define TOLERANCE 0 | |
| #endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS | |
| using LIBC_NAMESPACE::fputil::testing::ForceRoundingMode; | |
| using LIBC_NAMESPACE::fputil::testing::RoundingMode; | |
| class PowfTest : public LIBC_NAMESPACE::testing::FPTest<float> { | |
| public: | |
| void test_special_numbers(float (*func)(float, float), int tolerance = 0) { | |
| constexpr float neg_odd_integer = -3.0f; | |
| constexpr float neg_even_integer = -6.0f; | |
| constexpr float neg_non_integer = -1.1f; | |
| constexpr float pos_odd_integer = 5.0f; | |
| constexpr float pos_even_integer = 8.0f; | |
| constexpr float pos_non_integer = 1.3f; | |
| constexpr float one_half = 0.5f; | |
| for (int i = 0; i < N_ROUNDING_MODES; ++i) { | |
| ForceRoundingMode __r(ROUNDING_MODES[i]); | |
| if (!__r.success) | |
| continue; | |
| // pow( sNaN, exponent) | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN, sNaN), FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN, neg_odd_integer), | |
| FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN, neg_even_integer), | |
| FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN, pos_odd_integer), | |
| FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN, pos_even_integer), | |
| FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN, one_half), FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN, zero), FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN, neg_zero), FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN, inf), FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN, neg_inf), FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(sNaN, aNaN), FE_INVALID); | |
| // pow( 0.0f, exponent ) | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(zero, sNaN), FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(zero, neg_odd_integer), | |
| FE_DIVBYZERO); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(zero, neg_even_integer), | |
| FE_DIVBYZERO); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(zero, neg_non_integer), | |
| FE_DIVBYZERO); | |
| EXPECT_FP_EQ(zero, func(zero, pos_odd_integer)); | |
| EXPECT_FP_EQ(zero, func(zero, pos_even_integer)); | |
| EXPECT_FP_EQ(zero, func(zero, pos_non_integer)); | |
| EXPECT_FP_EQ(zero, func(zero, one_half)); | |
| EXPECT_FP_EQ(1.0f, func(zero, zero)); | |
| EXPECT_FP_EQ(1.0f, func(zero, neg_zero)); | |
| EXPECT_FP_EQ(0.0f, func(zero, inf)); | |
| EXPECT_FP_EQ(inf, func(zero, neg_inf)); | |
| EXPECT_FP_IS_NAN(func(zero, aNaN)); | |
| // pow( -0.0f, exponent ) | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(neg_zero, sNaN), FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(neg_inf, func(neg_zero, neg_odd_integer), | |
| FE_DIVBYZERO); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(neg_zero, neg_even_integer), | |
| FE_DIVBYZERO); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(neg_zero, neg_non_integer), | |
| FE_DIVBYZERO); | |
| EXPECT_FP_EQ(neg_zero, func(neg_zero, pos_odd_integer)); | |
| EXPECT_FP_EQ(zero, func(neg_zero, pos_even_integer)); | |
| EXPECT_FP_EQ(zero, func(neg_zero, pos_non_integer)); | |
| EXPECT_FP_EQ(zero, func(neg_zero, one_half)); | |
| EXPECT_FP_EQ(1.0f, func(neg_zero, zero)); | |
| EXPECT_FP_EQ(1.0f, func(neg_zero, neg_zero)); | |
| EXPECT_FP_EQ(0.0f, func(neg_zero, inf)); | |
| EXPECT_FP_EQ(inf, func(neg_zero, neg_inf)); | |
| EXPECT_FP_IS_NAN(func(neg_zero, aNaN)); | |
| // pow( 1.0f, exponent ) | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(1.0f, sNaN), FE_INVALID); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, neg_odd_integer)); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, neg_even_integer)); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, neg_non_integer)); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, pos_odd_integer)); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, pos_even_integer)); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, pos_non_integer)); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, one_half)); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, zero)); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, neg_zero)); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, inf)); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, neg_inf)); | |
| EXPECT_FP_EQ(1.0f, func(1.0f, aNaN)); | |
| // pow( -1.0f, exponent ) | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(-1.0f, sNaN), FE_INVALID); | |
| EXPECT_FP_EQ(-1.0f, func(-1.0f, neg_odd_integer)); | |
| EXPECT_FP_EQ(1.0f, func(-1.0f, neg_even_integer)); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(-1.0f, neg_non_integer), | |
| FE_INVALID); | |
| EXPECT_FP_EQ(-1.0f, func(-1.0f, pos_odd_integer)); | |
| EXPECT_FP_EQ(1.0f, func(-1.0f, pos_even_integer)); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(-1.0f, pos_non_integer), | |
| FE_INVALID); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(-1.0f, one_half), FE_INVALID); | |
| EXPECT_FP_EQ(1.0f, func(-1.0f, zero)); | |
| EXPECT_FP_EQ(1.0f, func(-1.0f, neg_zero)); | |
| EXPECT_FP_EQ(1.0f, func(-1.0f, inf)); | |
| EXPECT_FP_EQ(1.0f, func(-1.0f, neg_inf)); | |
| EXPECT_FP_IS_NAN(func(-1.0f, aNaN)); | |
| // pow( inf, exponent ) | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(inf, sNaN), FE_INVALID); | |
| EXPECT_FP_EQ(0.0f, func(inf, neg_odd_integer)); | |
| EXPECT_FP_EQ(0.0f, func(inf, neg_even_integer)); | |
| EXPECT_FP_EQ(0.0f, func(inf, neg_non_integer)); | |
| EXPECT_FP_EQ(inf, func(inf, pos_odd_integer)); | |
| EXPECT_FP_EQ(inf, func(inf, pos_even_integer)); | |
| EXPECT_FP_EQ(inf, func(inf, pos_non_integer)); | |
| EXPECT_FP_EQ(inf, func(inf, one_half)); | |
| EXPECT_FP_EQ(1.0f, func(inf, zero)); | |
| EXPECT_FP_EQ(1.0f, func(inf, neg_zero)); | |
| EXPECT_FP_EQ(inf, func(inf, inf)); | |
| EXPECT_FP_EQ(0.0f, func(inf, neg_inf)); | |
| EXPECT_FP_IS_NAN(func(inf, aNaN)); | |
| // pow( -inf, exponent ) | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(neg_inf, sNaN), FE_INVALID); | |
| EXPECT_FP_EQ(-0.0f, func(neg_inf, neg_odd_integer)); | |
| EXPECT_FP_EQ(0.0f, func(neg_inf, neg_even_integer)); | |
| EXPECT_FP_EQ(0.0f, func(neg_inf, neg_non_integer)); | |
| EXPECT_FP_EQ(neg_inf, func(neg_inf, pos_odd_integer)); | |
| EXPECT_FP_EQ(inf, func(neg_inf, pos_even_integer)); | |
| EXPECT_FP_EQ(inf, func(neg_inf, pos_non_integer)); | |
| EXPECT_FP_EQ(inf, func(neg_inf, one_half)); | |
| EXPECT_FP_EQ(1.0f, func(neg_inf, zero)); | |
| EXPECT_FP_EQ(1.0f, func(neg_inf, neg_zero)); | |
| EXPECT_FP_EQ(inf, func(neg_inf, inf)); | |
| EXPECT_FP_EQ(0.0f, func(neg_inf, neg_inf)); | |
| EXPECT_FP_IS_NAN(func(neg_inf, aNaN)); | |
| // pow( aNaN, exponent ) | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(aNaN, sNaN), FE_INVALID); | |
| EXPECT_FP_IS_NAN(func(aNaN, neg_odd_integer)); | |
| EXPECT_FP_IS_NAN(func(aNaN, neg_even_integer)); | |
| EXPECT_FP_IS_NAN(func(aNaN, neg_non_integer)); | |
| EXPECT_FP_IS_NAN(func(aNaN, pos_odd_integer)); | |
| EXPECT_FP_IS_NAN(func(aNaN, pos_even_integer)); | |
| EXPECT_FP_IS_NAN(func(aNaN, pos_non_integer)); | |
| EXPECT_FP_IS_NAN(func(aNaN, one_half)); | |
| EXPECT_FP_EQ(1.0f, func(aNaN, zero)); | |
| EXPECT_FP_EQ(1.0f, func(aNaN, neg_zero)); | |
| EXPECT_FP_IS_NAN(func(aNaN, inf)); | |
| EXPECT_FP_IS_NAN(func(aNaN, neg_inf)); | |
| EXPECT_FP_IS_NAN(func(aNaN, aNaN)); | |
| // Exact powers of 2: | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(2.0f, sNaN), FE_INVALID); | |
| EXPECT_FP_EQ(0x1.0p15f, func(2.0f, 15.0f)); | |
| EXPECT_FP_EQ(0x1.0p126f, func(2.0f, 126.0f)); | |
| EXPECT_FP_EQ(0x1.0p-45f, func(2.0f, -45.0f)); | |
| EXPECT_FP_EQ(0x1.0p-126f, func(2.0f, -126.0f)); | |
| EXPECT_FP_EQ(0x1.0p-149f, func(2.0f, -149.0f)); | |
| // Powers of 10: | |
| EXPECT_FP_EQ(1.0f, func(10.0f, 0.0f)); | |
| if (tolerance == 0) { | |
| EXPECT_FP_EQ(10.0f, func(10.0f, 1.0f)); | |
| EXPECT_FP_EQ(100.0f, func(10.0f, 2.0f)); | |
| EXPECT_FP_EQ(1000.0f, func(10.0f, 3.0f)); | |
| EXPECT_FP_EQ(10000.0f, func(10.0f, 4.0f)); | |
| EXPECT_FP_EQ(100000.0f, func(10.0f, 5.0f)); | |
| EXPECT_FP_EQ(1000000.0f, func(10.0f, 6.0f)); | |
| EXPECT_FP_EQ(10000000.0f, func(10.0f, 7.0f)); | |
| EXPECT_FP_EQ(100000000.0f, func(10.0f, 8.0f)); | |
| EXPECT_FP_EQ(1000000000.0f, func(10.0f, 9.0f)); | |
| EXPECT_FP_EQ(10000000000.0f, func(10.0f, 10.0f)); | |
| } else { | |
| auto check_close = [tolerance](float act, float exp) { | |
| uint32_t a = FPBits(act).uintval(); | |
| uint32_t e = FPBits(exp).uintval(); | |
| EXPECT_LE(a >= e ? a - e : e - a, static_cast<uint32_t>(tolerance)); | |
| }; | |
| check_close(func(10.0f, 1.0f), 10.0f); | |
| check_close(func(10.0f, 2.0f), 100.0f); | |
| check_close(func(10.0f, 3.0f), 1000.0f); | |
| check_close(func(10.0f, 4.0f), 10000.0f); | |
| check_close(func(10.0f, 5.0f), 100000.0f); | |
| check_close(func(10.0f, 6.0f), 1000000.0f); | |
| check_close(func(10.0f, 7.0f), 10000000.0f); | |
| check_close(func(10.0f, 8.0f), 100000000.0f); | |
| check_close(func(10.0f, 9.0f), 1000000000.0f); | |
| check_close(func(10.0f, 10.0f), 10000000000.0f); | |
| } | |
| EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, func(10.0f, sNaN), FE_INVALID); | |
| // Overflow / Underflow: | |
| if (ROUNDING_MODES[i] != RoundingMode::Downward && | |
| ROUNDING_MODES[i] != RoundingMode::TowardZero) { | |
| EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(3.1f, 201.0f), FE_OVERFLOW); | |
| } | |
| if (ROUNDING_MODES[i] != RoundingMode::Upward) { | |
| EXPECT_FP_EQ_WITH_EXCEPTION(0.0f, func(3.1f, -201.0f), FE_UNDERFLOW); | |
| } | |
| } | |
| EXPECT_FP_EQ(-0.0f, func(-0.015625f, 25.0f)); | |
| EXPECT_FP_EQ(0.0f, func(-0.015625f, 26.0f)); | |
| } | |
| void test_subnormal_base(float (*func)(float, float), int tolerance = 0) { | |
| if (tolerance == 0) { | |
| EXPECT_FP_EQ(0x1.0p-32f, func(0x1.0p-128f, 0.25f)); | |
| EXPECT_FP_EQ(0x1.0p96f, func(0x1.0p-128f, -0.75f)); | |
| EXPECT_FP_EQ(0x1.90a962p-33f, func(0x1.8p-130f, 0.25f)); | |
| EXPECT_FP_EQ(0x1.47238cp+32f, func(0x1.8p-130f, -0.25f)); | |
| } else { | |
| auto check_close = [tolerance](float act, float exp) { | |
| uint32_t a = FPBits(act).uintval(); | |
| uint32_t e = FPBits(exp).uintval(); | |
| EXPECT_LE(a >= e ? a - e : e - a, static_cast<uint32_t>(tolerance)); | |
| }; | |
| check_close(func(0x1.0p-128f, 0.25f), 0x1.0p-32f); | |
| check_close(func(0x1.0p-128f, -0.75f), 0x1.0p96f); | |
| check_close(func(0x1.8p-130f, 0.25f), 0x1.90a962p-33f); | |
| check_close(func(0x1.8p-130f, -0.25f), 0x1.47238cp+32f); | |
| } | |
| } | |
| void test_range_underflow(float (*func)(float, float)) { | |
| constexpr float BASES[] = {0x1.8p-2f, 0x1.fffffep-2f, 0x1.8p-130f}; | |
| constexpr float EXPONENTS[] = {150.0f, 150.0f, 1.25f}; | |
| for (int mode = 0; mode < N_ROUNDING_MODES; ++mode) { | |
| ForceRoundingMode rounding(ROUNDING_MODES[mode]); | |
| if (!rounding.success) | |
| continue; | |
| float positive = | |
| ROUNDING_MODES[mode] == RoundingMode::Upward ? min_denormal : zero; | |
| float negative = ROUNDING_MODES[mode] == RoundingMode::Downward | |
| ? -min_denormal | |
| : neg_zero; | |
| for (unsigned index = 0; index < 3; ++index) | |
| EXPECT_FP_EQ_WITH_EXCEPTION(positive, | |
| func(BASES[index], EXPONENTS[index]), | |
| FE_UNDERFLOW | FE_INEXACT); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(negative, func(-0x1.8p-2f, 151.0f), | |
| FE_UNDERFLOW | FE_INEXACT); | |
| EXPECT_FP_EQ_WITH_EXCEPTION(negative, func(-0x1.8p-130f, 3.0f), | |
| FE_UNDERFLOW | FE_INEXACT); | |
| float boundary = ROUNDING_MODES[mode] == RoundingMode::Upward || | |
| ROUNDING_MODES[mode] == RoundingMode::Nearest | |
| ? min_denormal | |
| : zero; | |
| EXPECT_FP_EQ_WITH_EXCEPTION(boundary, func(0x1.fffffep-2f, 0x1.2bfffep7f), | |
| FE_UNDERFLOW | FE_INEXACT); | |
| constexpr float SUBNORMAL_BOUND = 150.0f / 126.0f; | |
| uint32_t bound_bits = FPBits(SUBNORMAL_BOUND).uintval(); | |
| EXPECT_FP_EQ_WITH_EXCEPTION( | |
| boundary, func(0x1.fffffcp-127f, FPBits(bound_bits - 1).get_val()), | |
| FE_UNDERFLOW | FE_INEXACT); | |
| EXPECT_FP_EQ_WITH_EXCEPTION( | |
| positive, func(0x1.fffffcp-127f, FPBits(bound_bits + 1).get_val()), | |
| FE_UNDERFLOW | FE_INEXACT); | |
| } | |
| } | |
| #ifdef LIBC_TEST_FTZ_DAZ | |
| void test_ftz(float (*func)(float, float)) { | |
| LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::FTZ); | |
| volatile float x = -min_denormal; | |
| volatile float y = 0.5f; | |
| EXPECT_FP_IS_NAN(func(x, y)); | |
| volatile float two = 2.0f; | |
| volatile float d = min_denormal; | |
| EXPECT_FP_EQ(1.0f, func(two, d)); | |
| } | |
| void test_daz(float (*func)(float, float)) { | |
| LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::DAZ); | |
| volatile float x = -min_denormal; | |
| volatile float y = 0.5f; | |
| EXPECT_FP_EQ(0.0f, func(x, y)); | |
| volatile float two = 2.0f; | |
| volatile float d = min_denormal; | |
| EXPECT_FP_EQ(1.0f, func(two, d)); | |
| } | |
| void test_ftzdaz(float (*func)(float, float)) { | |
| LIBC_NAMESPACE::testing::ModifyMXCSR mxcsr(LIBC_NAMESPACE::testing::FTZ | | |
| LIBC_NAMESPACE::testing::DAZ); | |
| volatile float x = -min_denormal; | |
| volatile float y = 0.5f; | |
| EXPECT_FP_EQ(0.0f, func(x, y)); | |
| volatile float two = 2.0f; | |
| volatile float d = min_denormal; | |
| EXPECT_FP_EQ(1.0f, func(two, d)); | |
| } | |
| #endif // LIBC_TEST_FTZ_DAZ | |
| }; | |
| #ifdef LIBC_TEST_FTZ_DAZ | |
| #define LIST_POWF_FTZ_DAZ_TESTS(suffix, func) \ | |
| TEST_F(LlvmLibcPowfTest##suffix, FTZMode) { test_ftz(&func); } \ | |
| TEST_F(LlvmLibcPowfTest##suffix, DAZMode) { test_daz(&func); } \ | |
| TEST_F(LlvmLibcPowfTest##suffix, FTZDAZMode) { test_ftzdaz(&func); } | |
| #else | |
| #define LIST_POWF_FTZ_DAZ_TESTS(suffix, func) | |
| #endif // LIBC_TEST_FTZ_DAZ | |
| #define LIST_POWF_TESTS(suffix, func, tolerance) \ | |
| using LlvmLibcPowfTest##suffix = PowfTest; \ | |
| TEST_F(LlvmLibcPowfTest##suffix, SpecialNumbers) { \ | |
| test_special_numbers(&func, tolerance); \ | |
| } \ | |
| TEST_F(LlvmLibcPowfTest##suffix, SubnormalBase) { \ | |
| test_subnormal_base(&func, tolerance); \ | |
| } \ | |
| TEST_F(LlvmLibcPowfTest##suffix, RangeUnderflow) { \ | |
| test_range_underflow(&func); \ | |
| } \ | |
| LIST_POWF_FTZ_DAZ_TESTS(suffix, func) \ | |
| static_assert(true, "Require semicolon.") | |
| LIST_POWF_TESTS(Default, LIBC_NAMESPACE::powf, TOLERANCE); | |
| LIST_POWF_TESTS(DoubleEval, LIBC_NAMESPACE::math::double_eval::powf, TOLERANCE); | |
| LIST_POWF_TESTS(FloatEval, LIBC_NAMESPACE::math::float_eval::powf, | |
| /*tolerance=*/1); |