//===-- Utility class to test different flavors of fma --------------------===// // // 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_TEST_SRC_MATH_FMATEST_H #define LLVM_LIBC_TEST_SRC_MATH_FMATEST_H #include "src/__support/CPP/type_traits.h" #include "src/__support/FPUtil/cast.h" #include "src/__support/macros/properties/types.h" #include "test/UnitTest/FEnvSafeTest.h" #include "test/UnitTest/FPMatcher.h" #include "test/UnitTest/Test.h" template class FmaTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest { struct OutConstants { DECLARE_SPECIAL_CONSTANTS(OutType) }; struct InConstants { DECLARE_SPECIAL_CONSTANTS(InType) }; using OutFPBits = typename OutConstants::FPBits; using OutStorageType = typename OutConstants::StorageType; using InFPBits = typename InConstants::FPBits; using InStorageType = typename InConstants::StorageType; static constexpr OutStorageType OUT_MIN_NORMAL_U = OutFPBits::min_normal().uintval(); static constexpr InStorageType IN_MIN_NORMAL_U = InFPBits::min_normal().uintval(); OutConstants out; InConstants in; const InType in_out_min_normal = LIBC_NAMESPACE::fputil::cast(out.min_normal); const InType in_out_min_denormal = LIBC_NAMESPACE::fputil::cast(out.min_denormal); public: using FmaFunc = OutType (*)(InType, InType, InType); void test_special_numbers(FmaFunc func) { EXPECT_FP_EQ(out.zero, func(in.zero, in.zero, in.zero)); EXPECT_FP_EQ(out.neg_zero, func(in.zero, in.neg_zero, in.neg_zero)); EXPECT_FP_EQ(out.inf, func(in.inf, in.inf, in.zero)); EXPECT_FP_EQ(out.neg_inf, func(in.neg_inf, in.inf, in.neg_inf)); EXPECT_FP_EQ(out.aNaN, func(in.inf, in.zero, in.zero)); EXPECT_FP_EQ(out.aNaN, func(in.inf, in.neg_inf, in.inf)); EXPECT_FP_EQ(out.aNaN, func(in.aNaN, in.zero, in.inf)); EXPECT_FP_EQ(out.aNaN, func(in.inf, in.neg_inf, in.aNaN)); // Test underflow rounding up. EXPECT_FP_EQ(OutFPBits(OutStorageType(2)).get_val(), func(InType(0.5), in_out_min_denormal, in_out_min_denormal)); if constexpr (sizeof(OutType) < sizeof(InType)) { EXPECT_FP_EQ(out.zero, func(InType(0.5), in.min_denormal, in.min_denormal)); } // Test underflow rounding down. OutType v = OutFPBits(static_cast(OUT_MIN_NORMAL_U + OutStorageType(1))) .get_val(); EXPECT_FP_EQ(v, func(InType(1) / InType(OUT_MIN_NORMAL_U << 1), LIBC_NAMESPACE::fputil::cast(v), in_out_min_normal)); if constexpr (sizeof(OutType) < sizeof(InType)) { InFPBits tmp = InFPBits::one(); tmp.set_biased_exponent(InFPBits::EXP_BIAS - InFPBits::FRACTION_LEN - 1); InType reciprocal_value = tmp.get_val(); InType v = InFPBits(static_cast(IN_MIN_NORMAL_U + InStorageType(1))) .get_val(); EXPECT_FP_EQ(out.min_normal, func(reciprocal_value, v, in_out_min_normal)); } // Test overflow. OutType z = out.max_normal; InType in_z = LIBC_NAMESPACE::fputil::cast(out.max_normal); #if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION) // Rounding modes other than the default might not be usable with float16. if constexpr (LIBC_NAMESPACE::cpp::is_same_v) EXPECT_FP_EQ(OutType(0.75) * z, func(InType(1.75), in_z, -in_z)); else #endif EXPECT_FP_EQ_ALL_ROUNDING(OutType(0.75) * z, func(InType(1.75), in_z, -in_z)); // Exact cancellation. EXPECT_FP_EQ_ROUNDING_NEAREST( out.zero, func(InType(3.0), InType(5.0), InType(-15.0))); EXPECT_FP_EQ_ROUNDING_UPWARD(out.zero, func(InType(3.0), InType(5.0), InType(-15.0))); EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO( out.zero, func(InType(3.0), InType(5.0), InType(-15.0))); EXPECT_FP_EQ_ROUNDING_DOWNWARD( out.neg_zero, func(InType(3.0), InType(5.0), InType(-15.0))); EXPECT_FP_EQ_ROUNDING_NEAREST( out.zero, func(InType(-3.0), InType(5.0), InType(15.0))); EXPECT_FP_EQ_ROUNDING_UPWARD(out.zero, func(InType(-3.0), InType(5.0), InType(15.0))); EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO( out.zero, func(InType(-3.0), InType(5.0), InType(15.0))); EXPECT_FP_EQ_ROUNDING_DOWNWARD( out.neg_zero, func(InType(-3.0), InType(5.0), InType(15.0))); } }; #define LIST_FMA_TESTS(T, func) \ using LlvmLibcFmaTest = FmaTestTemplate; \ TEST_F(LlvmLibcFmaTest, SpecialNumbers) { test_special_numbers(&func); } #define LIST_NARROWING_FMA_TESTS(OutType, InType, func) \ using LlvmLibcFmaTest = FmaTestTemplate; \ TEST_F(LlvmLibcFmaTest, SpecialNumbers) { test_special_numbers(&func); } #endif // LLVM_LIBC_TEST_SRC_MATH_FMATEST_H