// Copyright 2019 Google LLC // SPDX-License-Identifier: Apache-2.0 // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "hwy/base.h" #include #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "base_test.cc" #include "hwy/foreach_target.h" // IWYU pragma: keep #include "hwy/highway.h" #include "hwy/tests/test_util-inl.h" HWY_BEFORE_NAMESPACE(); namespace hwy { namespace HWY_NAMESPACE { HWY_NOINLINE void TestAllLimits() { HWY_ASSERT_EQ(uint8_t{0}, LimitsMin()); HWY_ASSERT_EQ(uint16_t{0}, LimitsMin()); HWY_ASSERT_EQ(uint32_t{0}, LimitsMin()); HWY_ASSERT_EQ(uint64_t{0}, LimitsMin()); HWY_ASSERT_EQ(int8_t{-128}, LimitsMin()); HWY_ASSERT_EQ(int16_t{-32768}, LimitsMin()); HWY_ASSERT_EQ(static_cast(0x80000000u), LimitsMin()); HWY_ASSERT_EQ(static_cast(0x8000000000000000ull), LimitsMin()); HWY_ASSERT_EQ(uint8_t{0xFF}, LimitsMax()); HWY_ASSERT_EQ(uint16_t{0xFFFF}, LimitsMax()); HWY_ASSERT_EQ(uint32_t{0xFFFFFFFFu}, LimitsMax()); HWY_ASSERT_EQ(uint64_t{0xFFFFFFFFFFFFFFFFull}, LimitsMax()); HWY_ASSERT_EQ(int8_t{0x7F}, LimitsMax()); HWY_ASSERT_EQ(int16_t{0x7FFF}, LimitsMax()); HWY_ASSERT_EQ(int32_t{0x7FFFFFFFu}, LimitsMax()); HWY_ASSERT_EQ(int64_t{0x7FFFFFFFFFFFFFFFull}, LimitsMax()); HWY_ASSERT(LimitsMin() == LimitsMin()); HWY_ASSERT(LimitsMin() <= LimitsMin()); // NOLINT HWY_ASSERT(LimitsMin() <= LimitsMin()); HWY_ASSERT(LimitsMin() <= LimitsMin()); // NOLINT HWY_ASSERT(LimitsMin() <= LimitsMin()); // NOLINT HWY_ASSERT(LimitsMax() == LimitsMax()); HWY_ASSERT(LimitsMax() >= LimitsMax()); // NOLINT HWY_ASSERT(LimitsMax() >= LimitsMax()); HWY_ASSERT(LimitsMax() >= LimitsMax()); // NOLINT HWY_ASSERT(LimitsMax() >= LimitsMax()); // NOLINT HWY_ASSERT_EQ(static_cast(0), LimitsMin()); HWY_ASSERT_EQ(static_cast(0), LimitsMin()); HWY_ASSERT_EQ(0u, LimitsMin()); HWY_ASSERT_EQ(0ul, LimitsMin()); // NOLINT HWY_ASSERT_EQ(0ull, LimitsMin()); // NOLINT HWY_ASSERT(LimitsMax() == LimitsMax()); HWY_ASSERT(LimitsMax() >= LimitsMax()); // NOLINT HWY_ASSERT(LimitsMax() >= LimitsMax()); HWY_ASSERT(LimitsMax() >= LimitsMax()); // NOLINT // NOLINTNEXTLINE HWY_ASSERT(LimitsMax() >= LimitsMax()); HWY_ASSERT(LimitsMin() == 0 || LimitsMin() == LimitsMin()); HWY_ASSERT(LimitsMax() == LimitsMax() || LimitsMax() == LimitsMax()); HWY_ASSERT_EQ(size_t{0}, LimitsMin()); HWY_ASSERT(LimitsMin() < ptrdiff_t{0}); HWY_ASSERT(LimitsMin() < intptr_t{0}); HWY_ASSERT_EQ(uintptr_t{0}, LimitsMin()); HWY_ASSERT(LimitsMin() <= wchar_t{0}); HWY_ASSERT(LimitsMax() > size_t{0}); HWY_ASSERT(LimitsMax() > ptrdiff_t{0}); HWY_ASSERT(LimitsMax() > intptr_t{0}); HWY_ASSERT(LimitsMax() > uintptr_t{0}); HWY_ASSERT(LimitsMax() > wchar_t{0}); } struct TestLowestHighest { template HWY_NOINLINE void operator()(T /*unused*/) const { // numeric_limits::lowest is only guaranteed to be what we expect (-max) // for built-in floating-point types. if (!IsSpecialFloat()) { HWY_ASSERT_EQ(std::numeric_limits::lowest(), LowestValue()); HWY_ASSERT_EQ(std::numeric_limits::max(), HighestValue()); } } }; HWY_NOINLINE void TestAllLowestHighest() { ForAllTypes(TestLowestHighest()); } struct TestIsUnsigned { template HWY_NOINLINE void operator()(T /*unused*/) const { static_assert(!IsFloat(), "Expected !IsFloat"); static_assert(!IsSigned(), "Expected !IsSigned"); static_assert(IsInteger(), "Expected IsInteger"); } }; struct TestIsSigned { template HWY_NOINLINE void operator()(T /*unused*/) const { static_assert(!IsFloat(), "Expected !IsFloat"); static_assert(IsSigned(), "Expected IsSigned"); static_assert(IsInteger(), "Expected IsInteger"); } }; struct TestIsFloat { template HWY_NOINLINE void operator()(T /*unused*/) const { static_assert(IsFloat(), "Expected IsFloat"); static_assert(!IsInteger(), "Expected !IsInteger"); static_assert(IsSigned(), "Floats are also considered signed"); } }; HWY_NOINLINE void TestAllType() { const TestIsUnsigned is_unsigned_test; const TestIsSigned is_signed_test; ForUnsignedTypes(is_unsigned_test); ForSignedTypes(is_signed_test); ForFloatTypes(TestIsFloat()); is_unsigned_test(static_cast(0)); is_unsigned_test(static_cast(0)); is_unsigned_test(0u); is_unsigned_test(0ul); is_unsigned_test(0ull); is_unsigned_test(size_t{0}); is_unsigned_test(uintptr_t{0}); is_signed_test(static_cast(0)); is_signed_test(static_cast(0)); is_signed_test(0); is_signed_test(0L); is_signed_test(0LL); is_signed_test(ptrdiff_t{0}); is_signed_test(intptr_t{0}); static_assert(!IsFloat(), "Expected !IsFloat()"); static_assert(!IsFloat(), "Expected !IsFloat()"); static_assert(IsInteger(), "Expected IsInteger()"); static_assert(IsInteger(), "Expected IsInteger()"); static_assert(sizeof(MakeUnsigned) == 16, ""); static_assert(sizeof(MakeWide) == 16, "Expected uint128_t"); static_assert(sizeof(MakeNarrow) == 8, "Expected uint64_t"); } struct TestIsSame { template HWY_NOINLINE void operator()(T /*unused*/) const { static_assert(IsSame(), "T == T"); static_assert(!IsSame, MakeUnsigned>(), "S != U"); static_assert(!IsSame, MakeSigned>(), "U != S"); } }; HWY_NOINLINE void TestAllIsSame() { ForAllTypes(TestIsSame()); } HWY_NOINLINE void TestAllBitScan() { HWY_ASSERT_EQ(size_t{0}, Num0BitsAboveMS1Bit_Nonzero32(0x80000000u)); HWY_ASSERT_EQ(size_t{0}, Num0BitsAboveMS1Bit_Nonzero32(0xFFFFFFFFu)); HWY_ASSERT_EQ(size_t{1}, Num0BitsAboveMS1Bit_Nonzero32(0x40000000u)); HWY_ASSERT_EQ(size_t{1}, Num0BitsAboveMS1Bit_Nonzero32(0x40108210u)); HWY_ASSERT_EQ(size_t{30}, Num0BitsAboveMS1Bit_Nonzero32(2u)); HWY_ASSERT_EQ(size_t{30}, Num0BitsAboveMS1Bit_Nonzero32(3u)); HWY_ASSERT_EQ(size_t{31}, Num0BitsAboveMS1Bit_Nonzero32(1u)); HWY_ASSERT_EQ(size_t{0}, Num0BitsAboveMS1Bit_Nonzero64(0x8000000000000000ull)); HWY_ASSERT_EQ(size_t{0}, Num0BitsAboveMS1Bit_Nonzero64(0xFFFFFFFFFFFFFFFFull)); HWY_ASSERT_EQ(size_t{1}, Num0BitsAboveMS1Bit_Nonzero64(0x4000000000000000ull)); HWY_ASSERT_EQ(size_t{1}, Num0BitsAboveMS1Bit_Nonzero64(0x4010821004200011ull)); HWY_ASSERT_EQ(size_t{62}, Num0BitsAboveMS1Bit_Nonzero64(2ull)); HWY_ASSERT_EQ(size_t{62}, Num0BitsAboveMS1Bit_Nonzero64(3ull)); HWY_ASSERT_EQ(size_t{63}, Num0BitsAboveMS1Bit_Nonzero64(1ull)); HWY_ASSERT_EQ(size_t{0}, Num0BitsBelowLS1Bit_Nonzero32(1u)); HWY_ASSERT_EQ(size_t{1}, Num0BitsBelowLS1Bit_Nonzero32(2u)); HWY_ASSERT_EQ(size_t{30}, Num0BitsBelowLS1Bit_Nonzero32(0xC0000000u)); HWY_ASSERT_EQ(size_t{31}, Num0BitsBelowLS1Bit_Nonzero32(0x80000000u)); HWY_ASSERT_EQ(size_t{0}, Num0BitsBelowLS1Bit_Nonzero64(1ull)); HWY_ASSERT_EQ(size_t{1}, Num0BitsBelowLS1Bit_Nonzero64(2ull)); HWY_ASSERT_EQ(size_t{62}, Num0BitsBelowLS1Bit_Nonzero64(0xC000000000000000ull)); HWY_ASSERT_EQ(size_t{63}, Num0BitsBelowLS1Bit_Nonzero64(0x8000000000000000ull)); } HWY_NOINLINE void TestAllPopCount() { HWY_ASSERT_EQ(size_t{0}, PopCount(0u)); HWY_ASSERT_EQ(size_t{1}, PopCount(1u)); HWY_ASSERT_EQ(size_t{1}, PopCount(2u)); HWY_ASSERT_EQ(size_t{2}, PopCount(3u)); HWY_ASSERT_EQ(size_t{1}, PopCount(0x80000000u)); HWY_ASSERT_EQ(size_t{31}, PopCount(0x7FFFFFFFu)); HWY_ASSERT_EQ(size_t{32}, PopCount(0xFFFFFFFFu)); HWY_ASSERT_EQ(size_t{1}, PopCount(0x80000000ull)); HWY_ASSERT_EQ(size_t{31}, PopCount(0x7FFFFFFFull)); HWY_ASSERT_EQ(size_t{32}, PopCount(0xFFFFFFFFull)); HWY_ASSERT_EQ(size_t{33}, PopCount(0x10FFFFFFFFull)); HWY_ASSERT_EQ(size_t{63}, PopCount(0xFFFEFFFFFFFFFFFFull)); HWY_ASSERT_EQ(size_t{64}, PopCount(0xFFFFFFFFFFFFFFFFull)); } template static HWY_INLINE T TestEndianGetIntegerVal(T val) { static_assert(!IsFloat() && !IsSpecialFloat(), "T must not be a floating-point type"); using TU = MakeUnsigned; static_assert(sizeof(T) == sizeof(TU), "sizeof(T) == sizeof(TU) must be true"); uint8_t result_bytes[sizeof(T)]; const TU val_u = static_cast(val); for (size_t i = 0; i < sizeof(T); i++) { #if HWY_IS_BIG_ENDIAN const size_t shift_amt = (sizeof(T) - 1 - i) * 8; #else const size_t shift_amt = i * 8; #endif result_bytes[i] = static_cast((val_u >> shift_amt) & 0xFF); } T result; CopyBytes(result_bytes, &result); return result; } template static HWY_INLINE T TestEndianCreateValueFromBytes(Bytes&&... bytes) { static_assert(sizeof(T) > 0, "sizeof(T) > 0 must be true"); static_assert(sizeof...(Bytes) == sizeof(T), "sizeof...(Bytes) == sizeof(T) must be true"); const uint8_t src_bytes[sizeof(T)]{static_cast(bytes)...}; T result; CopyBytes(src_bytes, &result); return result; } #define HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(val) \ HWY_ASSERT_EQ(val, TestEndianGetIntegerVal(val)) HWY_NOINLINE void TestAllEndian() { HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(int8_t{0x01}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(uint8_t{0x01}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(int16_t{0x0102}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(uint16_t{0x0102}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(int32_t{0x01020304}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(uint32_t{0x01020304}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(int64_t{0x0102030405060708}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(uint64_t{0x0102030405060708}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(int16_t{0x0201}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(uint16_t{0x0201}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(int32_t{0x04030201}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(uint32_t{0x04030201}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(int64_t{0x0807060504030201}); HWY_TEST_ENDIAN_CHECK_INTEGER_VAL(uint64_t{0x0807060504030201}); HWY_ASSERT_EQ(HWY_IS_BIG_ENDIAN ? int16_t{0x0102} : int16_t{0x0201}, TestEndianCreateValueFromBytes(0x01, 0x02)); HWY_ASSERT_EQ(HWY_IS_BIG_ENDIAN ? uint16_t{0x0102} : uint16_t{0x0201}, TestEndianCreateValueFromBytes(0x01, 0x02)); HWY_ASSERT_EQ( HWY_IS_BIG_ENDIAN ? int32_t{0x01020304} : int32_t{0x04030201}, TestEndianCreateValueFromBytes(0x01, 0x02, 0x03, 0x04)); HWY_ASSERT_EQ( HWY_IS_BIG_ENDIAN ? uint32_t{0x01020304} : uint32_t{0x04030201}, TestEndianCreateValueFromBytes(0x01, 0x02, 0x03, 0x04)); HWY_ASSERT_EQ(HWY_IS_BIG_ENDIAN ? int64_t{0x0102030405060708} : int64_t{0x0807060504030201}, TestEndianCreateValueFromBytes( 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)); HWY_ASSERT_EQ(HWY_IS_BIG_ENDIAN ? uint64_t{0x0102030405060708} : uint64_t{0x0807060504030201}, TestEndianCreateValueFromBytes( 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)); HWY_ASSERT_EQ(HWY_IS_BIG_ENDIAN ? int16_t{-0x5EFE} : int16_t{0x02A1}, TestEndianCreateValueFromBytes(0xA1, 0x02)); HWY_ASSERT_EQ( HWY_IS_BIG_ENDIAN ? int32_t{-0x5E4D3CFC} : int32_t{0x04C3B2A1}, TestEndianCreateValueFromBytes(0xA1, 0xB2, 0xC3, 0x04)); HWY_ASSERT_EQ(HWY_IS_BIG_ENDIAN ? int64_t{-0x6E5D4C3B2A1908F8} : int64_t{0x08F7E6D5C4B3A291}, TestEndianCreateValueFromBytes( 0x91, 0xA2, 0xB3, 0xC4, 0xD5, 0xE6, 0xF7, 0x08)); HWY_ASSERT_EQ(HWY_IS_LITTLE_ENDIAN ? int16_t{-0x5DFF} : int16_t{0x01A2}, TestEndianCreateValueFromBytes(0x01, 0xA2)); HWY_ASSERT_EQ( HWY_IS_LITTLE_ENDIAN ? int32_t{-0x3B4C5DFF} : int32_t{0x01A2B3C4}, TestEndianCreateValueFromBytes(0x01, 0xA2, 0xB3, 0xC4)); HWY_ASSERT_EQ(HWY_IS_LITTLE_ENDIAN ? int64_t{-0x0718293A4B5C6DFF} : int64_t{0x0192A3B4C5D6E7F8}, TestEndianCreateValueFromBytes( 0x01, 0x92, 0xA3, 0xB4, 0xC5, 0xD6, 0xE7, 0xF8)); #if HWY_IS_BIG_ENDIAN HWY_ASSERT_EQ(1.0f, TestEndianCreateValueFromBytes(0x3F, 0x80, 0x00, 0x00)); HWY_ASSERT_EQ(15922433.0f, TestEndianCreateValueFromBytes(0x4B, 0x72, 0xF5, 0x01)); HWY_ASSERT_EQ(-12357485.0f, TestEndianCreateValueFromBytes(0xCB, 0x3C, 0x8F, 0x6D)); #else HWY_ASSERT_EQ(1.0f, TestEndianCreateValueFromBytes(0x00, 0x00, 0x80, 0x3F)); HWY_ASSERT_EQ(15922433.0f, TestEndianCreateValueFromBytes(0x01, 0xF5, 0x72, 0x4B)); HWY_ASSERT_EQ(-12357485.0f, TestEndianCreateValueFromBytes(0x6D, 0x8F, 0x3C, 0xCB)); #endif #if HWY_HAVE_FLOAT64 #if HWY_IS_BIG_ENDIAN HWY_ASSERT_EQ(1.0, TestEndianCreateValueFromBytes( 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); HWY_ASSERT_EQ(8707235690688195.0, TestEndianCreateValueFromBytes(0x43, 0x3E, 0xEF, 0x2F, 0x4A, 0x51, 0xAE, 0xC3)); HWY_ASSERT_EQ(-6815854340348452.0, TestEndianCreateValueFromBytes(0xC3, 0x38, 0x36, 0xFB, 0xC0, 0xCC, 0x1A, 0x24)); #else HWY_ASSERT_EQ(1.0, TestEndianCreateValueFromBytes( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F)); HWY_ASSERT_EQ(8707235690688195.0, TestEndianCreateValueFromBytes(0xC3, 0xAE, 0x51, 0x4A, 0x2F, 0xEF, 0x3E, 0x43)); HWY_ASSERT_EQ(-6815854340348452.0, TestEndianCreateValueFromBytes(0x24, 0x1A, 0xCC, 0xC0, 0xFB, 0x36, 0x38, 0xC3)); #endif // HWY_IS_BIG_ENDIAN #endif // HWY_HAVE_FLOAT64 #if HWY_IS_BIG_ENDIAN HWY_ASSERT_EQ(ConvertScalarTo(1.0f), BitCastScalar( TestEndianCreateValueFromBytes(0x3F, 0x80))); HWY_ASSERT_EQ(ConvertScalarTo(0.333984375f), BitCastScalar( TestEndianCreateValueFromBytes(0x3E, 0xAB))); HWY_ASSERT_EQ(ConvertScalarTo(167121905303526337111381770240.0f), BitCastScalar( TestEndianCreateValueFromBytes(0x70, 0x07))); #else HWY_ASSERT_EQ(ConvertScalarTo(1.0f), BitCastScalar( TestEndianCreateValueFromBytes(0x80, 0x3F))); HWY_ASSERT_EQ(ConvertScalarTo(0.333984375f), BitCastScalar( TestEndianCreateValueFromBytes(0xAB, 0x3E))); HWY_ASSERT_EQ(ConvertScalarTo(167121905303526337111381770240.0f), BitCastScalar( TestEndianCreateValueFromBytes(0x07, 0x70))); #endif } struct TestSpecialFloat { template static constexpr bool EnableSpecialFloatArithOpTest() { return (hwy::IsSame() && HWY_HAVE_SCALAR_F16_OPERATORS) || (hwy::IsSame() && HWY_HAVE_SCALAR_BF16_OPERATORS); } template static constexpr HWY_INLINE T EnsureNotNativeSpecialFloat(T&& val) { #if HWY_HAVE_SCALAR_F16_TYPE static_assert(!hwy::IsSame, float16_t::Native>(), "The operator must not return a float16_t::Native"); #endif #if HWY_HAVE_SCALAR_BF16_TYPE static_assert(!hwy::IsSame, bfloat16_t::Native>(), "The operator must not return a bfloat16_t::Native"); #endif return static_cast(val); } template static HWY_INLINE void AssertSpecialFloatOpResultInRange(float min_expected, float max_expected, T actual, const char* filename, const int line) { if (!(actual >= min_expected && actual <= max_expected)) { hwy::Abort( filename, line, "mismatch: value was expected to be between %g and %g, got %g\n", static_cast(min_expected), static_cast(max_expected), ConvertScalarTo(actual)); } } template ()>* = nullptr> static HWY_NOINLINE void TestSpecialFloatArithOperators(T /*unused*/) { #if HWY_HAVE_SCALAR_F16_OPERATORS || HWY_HAVE_SCALAR_BF16_OPERATORS AssertSpecialFloatOpResultInRange( -0.008422852f, -0.008361816f, EnsureNotNativeSpecialFloat(static_cast(-0.008911133f) + static_cast(5.264282E-4f)), __FILE__, __LINE__); AssertSpecialFloatOpResultInRange( 0.44335937f, 0.4453125f, EnsureNotNativeSpecialFloat(static_cast(-0.0014266968f) + 0.4453125f), __FILE__, __LINE__); AssertSpecialFloatOpResultInRange( 39.25f, 39.5f, EnsureNotNativeSpecialFloat(34.25f + static_cast(5.0625f)), __FILE__, __LINE__); AssertSpecialFloatOpResultInRange( 7456.0f, 7488.0f, EnsureNotNativeSpecialFloat(static_cast(0.29101562f) - static_cast(-7456.0f)), __FILE__, __LINE__); AssertSpecialFloatOpResultInRange( -2.21875f, -2.203125f, EnsureNotNativeSpecialFloat(static_cast(1.66893E-4f) - 2.21875f), __FILE__, __LINE__); AssertSpecialFloatOpResultInRange( 0.32421875f, 0.32617188f, EnsureNotNativeSpecialFloat(0.35351562f - static_cast(0.028198242f)), __FILE__, __LINE__); AssertSpecialFloatOpResultInRange( -0.01135254f, -0.011291503f, EnsureNotNativeSpecialFloat(static_cast(2.109375f) * static_cast(-0.0053710938f)), __FILE__, __LINE__); AssertSpecialFloatOpResultInRange( 2.359375f, 2.375f, EnsureNotNativeSpecialFloat(static_cast(0.0019454956f) * 1216.0f), __FILE__, __LINE__); AssertSpecialFloatOpResultInRange( -1.1014938E-4f, 3.453125f, EnsureNotNativeSpecialFloat(-0.00038146973f * static_cast(-0.00037956237f)), __FILE__, __LINE__); AssertSpecialFloatOpResultInRange( -27.875f, -27.75f, EnsureNotNativeSpecialFloat(static_cast(-56.5f) / static_cast(2.03125f)), __FILE__, __LINE__); AssertSpecialFloatOpResultInRange( 0.033203125f, 0.033447266f, EnsureNotNativeSpecialFloat(static_cast(470.0f) / 14080.0f), __FILE__, __LINE__); AssertSpecialFloatOpResultInRange( 0.51953125f, 0.5234375f, EnsureNotNativeSpecialFloat(0.26367188f / static_cast(0.50390625f)), __FILE__, __LINE__); T incr_assign_result_1 = static_cast(1.373291E-4f); EnsureNotNativeSpecialFloat(incr_assign_result_1 += static_cast(-20.375f)); AssertSpecialFloatOpResultInRange(-20.375f, -20.25f, incr_assign_result_1, __FILE__, __LINE__); T incr_assign_result_2 = static_cast(2.1457672E-4f); EnsureNotNativeSpecialFloat(incr_assign_result_2 += static_cast(7)); AssertSpecialFloatOpResultInRange(7.0f, 7.03125f, incr_assign_result_2, __FILE__, __LINE__); T decr_assign_result_1 = static_cast(4.4059753E-4f); EnsureNotNativeSpecialFloat(decr_assign_result_1 -= static_cast(6880.0f)); AssertSpecialFloatOpResultInRange(-6880, -6848, decr_assign_result_1, __FILE__, __LINE__); T decr_assign_result_2 = static_cast(85.5f); EnsureNotNativeSpecialFloat(decr_assign_result_2 -= static_cast(5)); AssertSpecialFloatOpResultInRange(80.5f, 80.5f, decr_assign_result_2, __FILE__, __LINE__); T mul_assign_result_1 = static_cast(15680.0f); EnsureNotNativeSpecialFloat(mul_assign_result_1 *= static_cast(0.001373291f)); AssertSpecialFloatOpResultInRange(21.5f, 21.625f, mul_assign_result_1, __FILE__, __LINE__); T mul_assign_result_2 = static_cast(2.609375f); EnsureNotNativeSpecialFloat(mul_assign_result_2 *= static_cast(7)); AssertSpecialFloatOpResultInRange(18.25, 18.375, mul_assign_result_2, __FILE__, __LINE__); T div_assign_result_1 = static_cast(11584.0f); EnsureNotNativeSpecialFloat(div_assign_result_1 /= static_cast(9.5625f)); AssertSpecialFloatOpResultInRange(1208.0f, 1216.0f, div_assign_result_1, __FILE__, __LINE__); T div_assign_result_2 = static_cast(0.12109375f); EnsureNotNativeSpecialFloat(div_assign_result_2 /= static_cast(3)); AssertSpecialFloatOpResultInRange(0.040283203f, 0.040527344f, div_assign_result_2, __FILE__, __LINE__); HWY_ASSERT_EQ(static_cast(-1.0f), EnsureNotNativeSpecialFloat(-static_cast(1.0f))); HWY_ASSERT_EQ(static_cast(1.0f), EnsureNotNativeSpecialFloat(+static_cast(1.0f))); T pre_incr_result_1 = static_cast(1.0f); T pre_incr_result_2 = EnsureNotNativeSpecialFloat(++pre_incr_result_1); HWY_ASSERT_EQ(static_cast(2.0f), pre_incr_result_1); HWY_ASSERT_EQ(static_cast(2.0f), pre_incr_result_2); T post_incr_result_1 = static_cast(5.0f); T post_incr_result_2 = EnsureNotNativeSpecialFloat(post_incr_result_1++); HWY_ASSERT_EQ(static_cast(6.0f), post_incr_result_1); HWY_ASSERT_EQ(static_cast(5.0f), post_incr_result_2); T pre_decr_result_1 = static_cast(-2.0f); T pre_decr_result_2 = EnsureNotNativeSpecialFloat(--pre_decr_result_1); HWY_ASSERT_EQ(static_cast(-3.0f), pre_decr_result_1); HWY_ASSERT_EQ(static_cast(-3.0f), pre_decr_result_2); T post_decr_result_1 = static_cast(-7.0f); T post_decr_result_2 = EnsureNotNativeSpecialFloat(post_decr_result_1--); HWY_ASSERT_EQ(static_cast(-8.0f), post_decr_result_1); HWY_ASSERT_EQ(static_cast(-7.0f), post_decr_result_2); HWY_ASSERT(static_cast(1.0f) == 1.0f); HWY_ASSERT(static_cast(-2.40625f) != 0.0033416748f); HWY_ASSERT(static_cast(-3248.0f) < 0.0018997193f); HWY_ASSERT(static_cast(-27904.0f) <= -3.859375f); HWY_ASSERT(static_cast(1.078125f) > 0.010009765f); HWY_ASSERT(static_cast(45312.0f) >= 0.00024318695f); HWY_ASSERT(2.0f == static_cast(2.0f)); HWY_ASSERT(-5.78125f != static_cast(-15168.0f)); HWY_ASSERT(-0.056884766f < static_cast(0.000088214875f)); HWY_ASSERT(0.00008392333f <= static_cast(1384.0f)); HWY_ASSERT(21888.0f > static_cast(-2.578125f)); HWY_ASSERT(0.087402344 >= static_cast(-0.65625f)); #endif // HWY_HAVE_SCALAR_F16_OPERATORS || HWY_HAVE_SCALAR_BF16_OPERATORS } template ()>* = nullptr> static HWY_INLINE void TestSpecialFloatArithOperators(T /*unused*/) {} template HWY_NOINLINE void operator()(T /*unused*/) const { static_assert(IsSpecialFloat(), "IsSpecialFloat() must be true"); HWY_ASSERT_EQ(static_cast(0x436B0000u), BitCastScalar(ConvertScalarTo( BitCastScalar(static_cast( IsSame() ? 0x5B58u : 0x436Bu))))); HWY_ASSERT_EQ(static_cast(0xBB790000u), BitCastScalar(ConvertScalarTo( BitCastScalar(static_cast( IsSame() ? 0x9BC8u : 0xBB79u))))); HWY_ASSERT_EQ(static_cast( IsSame() ? 0xC0B86000u : 0xC5C30000u), BitCastScalar(ConvertScalarTo( BitCastScalar(static_cast(0xC5C3u))))); HWY_ASSERT_EQ(static_cast( IsSame() ? 0x41D20000u : 0x4E900000u), BitCastScalar(ConvertScalarTo( BitCastScalar(static_cast(0x4E90u))))); HWY_ASSERT_EQ(1696.0, ConvertScalarTo(ConvertScalarTo(1696.0f))); HWY_ASSERT_EQ( -0.00177001953125f, ConvertScalarTo(ConvertScalarTo(-0.00177001953125f))); HWY_ASSERT_EQ(0.49609375f, ConvertScalarTo(ConvertScalarTo(0.49609375))); HWY_ASSERT_EQ( 0.000553131103515625, ConvertScalarTo(ConvertScalarTo(0.000553131103515625))); HWY_ASSERT_EQ(ConvertScalarTo(3.0f), ConvertScalarTo(3)); HWY_ASSERT_EQ(ConvertScalarTo(-5.5f), ConvertScalarTo(-5.5)); HWY_ASSERT_EQ(ConvertScalarTo(0.82421875f), ConvertScalarTo(BF16FromF32(0.82421875f))); HWY_ASSERT_EQ(ConvertScalarTo(-6.375f), ConvertScalarTo(F16FromF32(-6.375f))); HWY_ASSERT(ConvertScalarTo(-3.671875f) < ConvertScalarTo(0.0218505859375f)); HWY_ASSERT(ConvertScalarTo(-0.033447265625f) <= ConvertScalarTo(8.249282836914062E-5f)); HWY_ASSERT(ConvertScalarTo(23296.0f) > ConvertScalarTo(192.0f)); HWY_ASSERT(ConvertScalarTo(41984.0f) >= ConvertScalarTo(370.0f)); TestSpecialFloatArithOperators(T()); } }; HWY_NOINLINE void TestAllSpecialFloat() { TestSpecialFloat test; test(float16_t()); test(bfloat16_t()); } // NOLINTNEXTLINE(google-readability-namespace-comments) } // namespace HWY_NAMESPACE } // namespace hwy HWY_AFTER_NAMESPACE(); #if HWY_ONCE namespace hwy { HWY_BEFORE_TEST(BaseTest); HWY_EXPORT_AND_TEST_P(BaseTest, TestAllLimits); HWY_EXPORT_AND_TEST_P(BaseTest, TestAllLowestHighest); HWY_EXPORT_AND_TEST_P(BaseTest, TestAllType); HWY_EXPORT_AND_TEST_P(BaseTest, TestAllIsSame); HWY_EXPORT_AND_TEST_P(BaseTest, TestAllBitScan); HWY_EXPORT_AND_TEST_P(BaseTest, TestAllPopCount); HWY_EXPORT_AND_TEST_P(BaseTest, TestAllEndian); HWY_EXPORT_AND_TEST_P(BaseTest, TestAllSpecialFloat); } // namespace hwy #endif