//===-- 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 "hdr/math_macros.h" #include "src/__support/CPP/simd.h" #include "src/__support/FPUtil/FPBits.h" #include "src/math/expf.h" #include "src/mathvec/expf.h" #include "test/UnitTest/SIMDMatcher.h" #include "test/UnitTest/Test.h" #include "hdr/stdint_proxy.h" using LlvmLibcVecExpfTest = LIBC_NAMESPACE::testing::FPTest; // Wrappers // In order to test vector we can either duplicate a scalar input // or do something more elaborate. In any case that requires a wrapper // since the function call is written in this file. // Run reference on a vector with lanes duplicated from a scalar input. // with control lane static LIBC_NAMESPACE::cpp::simd wrap_ref_vexpf(float x, float control) { LIBC_NAMESPACE::cpp::simd v(x); v[0] = control; constexpr size_t N = LIBC_NAMESPACE::cpp::internal::native_vector_size; for (size_t i = 0; i < N; i++) v[i] = LIBC_NAMESPACE::expf(v[i]); return v; } // without control lane static LIBC_NAMESPACE::cpp::simd wrap_ref_vexpf(float x) { return wrap_ref_vexpf(x, x); } // Run implementation on a vector with lanes duplicated from a scalar input. // with control lane static LIBC_NAMESPACE::cpp::simd wrap_vexpf(float x, float control) { LIBC_NAMESPACE::cpp::simd v(x); v[0] = control; return LIBC_NAMESPACE::expf(v); } // without control lane static LIBC_NAMESPACE::cpp::simd wrap_vexpf(float x) { return wrap_vexpf(x, x); } TEST_F(LlvmLibcVecExpfTest, SpecialNumbers) { EXPECT_SIMD_EQ(LIBC_NAMESPACE::cpp::splat(aNaN), wrap_vexpf(aNaN)); EXPECT_SIMD_EQ(LIBC_NAMESPACE::cpp::splat(inf), wrap_vexpf(inf)); EXPECT_SIMD_EQ(LIBC_NAMESPACE::cpp::splat(0.0f), wrap_vexpf(neg_inf)); EXPECT_SIMD_EQ(LIBC_NAMESPACE::cpp::splat(1.0f), wrap_vexpf(0.0f)); EXPECT_SIMD_EQ(LIBC_NAMESPACE::cpp::splat(1.0f), wrap_vexpf(-0.0f)); } TEST_F(LlvmLibcVecExpfTest, Overflow) { // Fails if tested with exceptions EXPECT_SIMD_EQ(LIBC_NAMESPACE::cpp::splat(inf), wrap_vexpf(FPBits(0x7f7fffffU).get_val())); EXPECT_SIMD_EQ(LIBC_NAMESPACE::cpp::splat(inf), wrap_vexpf(FPBits(0x42cffff8U).get_val())); EXPECT_SIMD_EQ(LIBC_NAMESPACE::cpp::splat(inf), wrap_vexpf(FPBits(0x42d00008U).get_val())); } TEST_F(LlvmLibcVecExpfTest, Underflow) { // Passes if tested with exceptions ? EXPECT_SIMD_EQ_WITH_EXCEPTION(LIBC_NAMESPACE::cpp::splat(0.0f), wrap_vexpf(FPBits(0xff7fffffU).get_val()), FE_UNDERFLOW); float x = FPBits(0xc2cffff8U).get_val(); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, 1.0), wrap_vexpf(x, 1.0)); x = FPBits(0xc2d00008U).get_val(); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, 1.0), wrap_vexpf(x, 1.0)); } // Test with inputs which are the borders of underflow/overflow but still // produce valid results without setting errno. // Is this still relevant to vector function? TEST_F(LlvmLibcVecExpfTest, Borderline) { float x; x = FPBits(0x42affff8U).get_val(); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, 1.0), wrap_vexpf(x, 1.0)); x = FPBits(0x42b00008U).get_val(); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, 1.0), wrap_vexpf(x, 1.0)); x = FPBits(0xc2affff8U).get_val(); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, 1.0), wrap_vexpf(x, 1.0)); x = FPBits(0xc2b00008U).get_val(); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, 1.0), wrap_vexpf(x, 1.0)); x = FPBits(0xc236bd8cU).get_val(); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, 1.0), wrap_vexpf(x, 1.0)); } TEST_F(LlvmLibcVecExpfTest, InFloatRange) { constexpr uint32_t COUNT = 100'000; constexpr uint32_t STEP = UINT32_MAX / COUNT; for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) { float x = FPBits(v).get_val(); if (FPBits(v).is_nan() || FPBits(v).is_inf()) continue; EXPECT_SIMD_EQ(wrap_ref_vexpf(x), wrap_vexpf(x)); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, aNaN), wrap_vexpf(x, aNaN)); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, inf), wrap_vexpf(x, inf)); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, -inf), wrap_vexpf(x, neg_inf)); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, 0.0), wrap_vexpf(x, 0.0)); EXPECT_SIMD_EQ(wrap_ref_vexpf(x, -0.0), wrap_vexpf(x, -0.0)); } }