diff --git a/conformity/opencv/common.h b/conformity/opencv/common.h index 351ba4ddfa345b075ceedbd6506170464e5dcba1..681f8a6e30e7bd8c486c7189faee224bc9f905b0 100644 --- a/conformity/opencv/common.h +++ b/conformity/opencv/common.h @@ -51,7 +51,7 @@ const float minusZero = -0.0F; const float oneNaN = floatval(0x7FC00001); const float zeroDivZero = -std::numeric_limits::quiet_NaN(); -const float floatMin = std::numeric_limits::min(); +const float floatMin = std::numeric_limits::lowest(); const float floatMax = std::numeric_limits::max(); const float posSubnormalMin = std::numeric_limits::denorm_min(); @@ -76,11 +76,6 @@ static float // clang-format on }; -template -static constexpr T min() { - return std::numeric_limits::min(); -} - template static constexpr T lowest() { return std::numeric_limits::lowest(); diff --git a/kleidicv/src/analysis/min_max_loc_neon.cpp b/kleidicv/src/analysis/min_max_loc_neon.cpp index 0bf04fe25f5b19258918fe7a7ded7a144b57d203..b41c9abaade8a46d10a2b3fade2a24a69962337b 100644 --- a/kleidicv/src/analysis/min_max_loc_neon.cpp +++ b/kleidicv/src/analysis/min_max_loc_neon.cpp @@ -112,10 +112,10 @@ class MinMaxLoc final : public UnrollTwice { vdupq_n(ScalarType{0}); vrunning_offset_ = vdupq_n(ScalarType{1}); vmin_ = vmin_new_ = vdupq_n(std::numeric_limits::max()); - vmax_ = vmax_new_ = vdupq_n(std::numeric_limits::min()); + vmax_ = vmax_new_ = vdupq_n(std::numeric_limits::lowest()); min_block_index_ = max_block_index_ = 0; min_vector_ = min_scalar_ = std::numeric_limits::max(); - max_vector_ = max_scalar_ = std::numeric_limits::min(); + max_vector_ = max_scalar_ = std::numeric_limits::lowest(); min_scalar_index_ = max_scalar_index_ = 0; running_index_ = 0; } diff --git a/kleidicv/src/arithmetics/add_abs_with_threshold_neon.cpp b/kleidicv/src/arithmetics/add_abs_with_threshold_neon.cpp index c8c944ea5df6413d92c4e8b00544f71f6c9248c8..ff26b7aa18f2691fe359d14ef2444bbcbf3c0710 100644 --- a/kleidicv/src/arithmetics/add_abs_with_threshold_neon.cpp +++ b/kleidicv/src/arithmetics/add_abs_with_threshold_neon.cpp @@ -38,7 +38,7 @@ class SaturatingAddAbsWithThreshold final : public UnrollOnce, private: ScalarType saturate_abs(ScalarType input) { if (std::numeric_limits::is_signed && - input == std::numeric_limits::min()) { + input == std::numeric_limits::lowest()) { return std::numeric_limits::max(); } return std::abs(input); diff --git a/kleidicv/src/arithmetics/add_neon.cpp b/kleidicv/src/arithmetics/add_neon.cpp index 50921be63e96cc166e3188b96fe73dad68a4c166..ff28c2b39e94a7ce3326891208817921c2a6093e 100644 --- a/kleidicv/src/arithmetics/add_neon.cpp +++ b/kleidicv/src/arithmetics/add_neon.cpp @@ -23,7 +23,7 @@ class SaturatingAdd final : public UnrollTwice { if (std::numeric_limits::is_signed && src_b < 0) { ScalarType result; return __builtin_add_overflow(src_a, src_b, &result) - ? std::numeric_limits::min() + ? std::numeric_limits::lowest() : result; } diff --git a/kleidicv/src/arithmetics/multiply_neon.cpp b/kleidicv/src/arithmetics/multiply_neon.cpp index 7488ea54ea71c6f0d9e2435f6bd5f3e1a69a5e1d..86af21c10598405905590f9ec962b92eb62210b9 100644 --- a/kleidicv/src/arithmetics/multiply_neon.cpp +++ b/kleidicv/src/arithmetics/multiply_neon.cpp @@ -46,7 +46,7 @@ class SaturatingMultiply final : public UnrollTwice { if (std::numeric_limits::is_signed) { if (__builtin_mul_overflow(src_a, src_b, &result)) { return (src_a < 0 && src_b > 0) || (src_a > 0 && src_b < 0) - ? std::numeric_limits::min() + ? std::numeric_limits::lowest() : std::numeric_limits::max(); } return result; diff --git a/kleidicv/src/arithmetics/sub_neon.cpp b/kleidicv/src/arithmetics/sub_neon.cpp index 4193a74e15496bf29684ca9621b546c9307ff87d..9af91ef73014a1c1356e04292f259044eb51028b 100644 --- a/kleidicv/src/arithmetics/sub_neon.cpp +++ b/kleidicv/src/arithmetics/sub_neon.cpp @@ -29,7 +29,7 @@ class SaturatingSub final : public UnrollTwice { ScalarType result; return __builtin_sub_overflow(src_a, src_b, &result) - ? std::numeric_limits::min() + ? std::numeric_limits::lowest() : result; } }; // end of class SaturatingSub diff --git a/kleidicv/src/conversions/float_conv_neon.cpp b/kleidicv/src/conversions/float_conv_neon.cpp index ba8c17cb6945b8a607dea5149d29e8f8de14a1bc..b5b82e6ae93da5693f7522060c7d4ca5b5d9c6ae 100644 --- a/kleidicv/src/conversions/float_conv_neon.cpp +++ b/kleidicv/src/conversions/float_conv_neon.cpp @@ -43,8 +43,8 @@ class float_conversion_operation { float f = std::nearbyint(src[ptrdiff_t(index)]); if (f > std::numeric_limits::max()) { f = std::numeric_limits::max(); - } else if (f < std::numeric_limits::min()) { - f = std::numeric_limits::min(); + } else if (f < std::numeric_limits::lowest()) { + f = std::numeric_limits::lowest(); } dst[index] = static_cast(f); } diff --git a/kleidicv/src/conversions/float_conv_sc.h b/kleidicv/src/conversions/float_conv_sc.h index d62bec2fd2bf534be7ff5b22ce9dac445511c4fc..35a429a831455f190ec77bb10062fdc4c666e84f 100644 --- a/kleidicv/src/conversions/float_conv_sc.h +++ b/kleidicv/src/conversions/float_conv_sc.h @@ -119,7 +119,7 @@ class float_conversion_operation { std::enable_if_t && std::is_signed_v, int> = 0> IntermediateVectorType remaining_path(svbool_t& pg, SrcVectorType src) KLEIDICV_STREAMING_COMPATIBLE { - constexpr float min_val = std::numeric_limits::min(); + constexpr float min_val = std::numeric_limits::lowest(); constexpr float max_val = std::numeric_limits::max(); src = svrinti_f32_x(pg, src); diff --git a/kleidicv_thread/src/kleidicv_thread.cpp b/kleidicv_thread/src/kleidicv_thread.cpp index e817530b53bb0d28819d261c69bf8e2fb8405873..2f4afebd2f50f171986335b920889e2401c0f637 100644 --- a/kleidicv_thread/src/kleidicv_thread.cpp +++ b/kleidicv_thread/src/kleidicv_thread.cpp @@ -239,7 +239,7 @@ kleidicv_error_t parallel_min_max(FunctionType min_max_func, std::vector min_values(height, std::numeric_limits::max()); std::vector max_values(height, - std::numeric_limits::min()); + std::numeric_limits::lowest()); parallel_min_max_data callback_data = { min_max_func, @@ -262,7 +262,7 @@ kleidicv_error_t parallel_min_max(FunctionType min_max_func, } } if (p_max_value) { - *p_max_value = std::numeric_limits::min(); + *p_max_value = std::numeric_limits::lowest(); for (ScalarType m : max_values) { if (m > *p_max_value) { *p_max_value = m; diff --git a/test/api/test_add_abs_with_threshold.cpp b/test/api/test_add_abs_with_threshold.cpp index bbabfb1ccb47cd7bd9501b9ea4954f4918072566..38f13f3a2cb3cc92ef859a5efaea1411c49cd1d6 100644 --- a/test/api/test_add_abs_with_threshold.cpp +++ b/test/api/test_add_abs_with_threshold.cpp @@ -66,18 +66,18 @@ template class SaturatingAddAbsWithThresholdTestMin final : public SaturatingAddAbsWithThresholdTestBase { using Elements = typename BinaryOperationTest::Elements; - using BinaryOperationTest::min; + using BinaryOperationTest::lowest; using BinaryOperationTest::max; - ElementType threshold() override { return min(); } + ElementType threshold() override { return lowest(); } const std::vector& test_elements() override { static const std::vector kTestElements = { // clang-format off - { min(), min(), max()}, - { min(), 0, max()}, - { min(), 1, max()}, - { min(), max(), max()}, + { lowest(), lowest(), max()}, + { lowest(), 0, max()}, + { lowest(), 1, max()}, + { lowest(), max(), max()}, // clang-format on }; return kTestElements; diff --git a/test/api/test_compare.cpp b/test/api/test_compare.cpp index b5b1230fea5edb50820742b63834541bb209a66a..a20dd21fabefc81a6f966c78716a5b195e34ebea 100644 --- a/test/api/test_compare.cpp +++ b/test/api/test_compare.cpp @@ -50,8 +50,8 @@ class CompareTestLinear final { } protected: - static constexpr ElementType min() { - return std::numeric_limits::min(); + static constexpr ElementType lowest() { + return std::numeric_limits::lowest(); } static constexpr ElementType max() { return std::numeric_limits::max(); @@ -74,7 +74,7 @@ class CompareTestLinear final { void test_linear(size_t width, size_t minimum_size) { size_t image_size = - std::max(minimum_size, static_cast(max() - min())); + std::max(minimum_size, static_cast(max() - lowest())); size_t height = image_size / width + 1; test::Array2D source_a(width, height, padding_, 1); test::Array2D source_b(width, height, padding_, 1); @@ -82,7 +82,7 @@ class CompareTestLinear final { test::Array2D actual = test::Array2D(width, height, padding_, 1); - GenerateLinearSeries generator_a(min()); + GenerateLinearSeries generator_a(lowest()); GenerateLinearSeries generator_b(128); source_a.fill(generator_a); @@ -166,13 +166,13 @@ template class CompareEqTestMin final : public CompareTestBase { using Elements = typename BinaryOperationTest::Elements; - using BinaryOperationTest::min; + using BinaryOperationTest::lowest; const std::vector& test_elements() override { static const std::vector kTestElements = { // clang-format off - { min(), min(), this->value()}, - {min() + 1, min(), 0}, + { lowest(), lowest(), this->value()}, + {lowest() + 1, lowest(), 0}, // clang-format on }; return kTestElements; @@ -233,13 +233,13 @@ template class CompareGtTestMin final : public CompareTestBase { using Elements = typename BinaryOperationTest::Elements; - using BinaryOperationTest::min; + using BinaryOperationTest::lowest; const std::vector& test_elements() override { static const std::vector kTestElements = { // clang-format off - { min(), min(), 0}, - {min() + 1, min(), this->value()}, + { lowest(), lowest(), 0}, + {lowest() + 1, lowest(), this->value()}, // clang-format on }; return kTestElements; diff --git a/test/api/test_float_conv.cpp b/test/api/test_float_conv.cpp index 5f3b0dda38e7f59bb6937f74cdedfdac373bcca5..ae7e2906e57876b5f703334febca1c93fb6775e3 100644 --- a/test/api/test_float_conv.cpp +++ b/test/api/test_float_conv.cpp @@ -25,8 +25,8 @@ template class FloatConversionTest final { private: template - static constexpr T min() { - return std::numeric_limits::min(); + static constexpr T lowest() { + return std::numeric_limits::lowest(); } template @@ -74,7 +74,7 @@ class FloatConversionTest final { const float oneNaN = floatval(0x7FC00001); const float zeroDivZero = -std::numeric_limits::quiet_NaN(); - const float floatMin = std::numeric_limits::min(); + const float floatMin = std::numeric_limits::lowest(); const float floatMax = std::numeric_limits::max(); const float posSubnormalMin = std::numeric_limits::denorm_min(); @@ -102,7 +102,7 @@ class FloatConversionTest final { {{ { 0, 0, 127, -128 }, { 0, 0, 0, 0 }, - { 0, 0, 0, 127 }, + { 0, 0, -128, 127 }, { 0, 0, 0, 0 }, { 127, -128, 113, 114 }, { 112, 113, 114, 115 }, @@ -154,10 +154,10 @@ class FloatConversionTest final { // clang-format off 5, 1, {{ - { min(), min() + 1, 0, max() - 1, max() } + { lowest(), lowest() + 1, 0, max() - 1, max() } }}, {{ - { static_cast(min()), static_cast(min()) + 1.0, 0, + { static_cast(lowest()), static_cast(lowest()) + 1.0, 0, static_cast(max()) - 1.0, static_cast(max()) } }} // clang-format on @@ -226,8 +226,8 @@ class FloatConversionTest final { // NOLINTEND(clang-analyzer-core.uninitialized.Assign) if (result > max()) { calculated = max(); - } else if (result < min()) { - calculated = min(); + } else if (result < lowest()) { + calculated = lowest(); } else { calculated = result; } @@ -252,8 +252,11 @@ class FloatConversionTest final { template size_t get_linear_height(size_t width, size_t minimum_size) { + // Calling this with a float type would generate a value of infinity. + static_assert(std::is_integral_v); + size_t image_size = - std::max(minimum_size, static_cast(max() - min())); + std::max(minimum_size, static_cast(max() - lowest())); size_t height = image_size / width + 1; return height; @@ -267,10 +270,10 @@ class FloatConversionTest final { test::Array2D actual(width, height, 1, 1); if constexpr (std::is_same_v && std::is_integral_v) { - test::GenerateLinearSeries generator(min()); + test::GenerateLinearSeries generator(lowest()); source.fill(generator); } else if constexpr (std::is_integral_v && std::is_same_v) { - test::GenerateLinearSeries generator(min()); + test::GenerateLinearSeries generator(lowest()); source.fill(generator); } else { static_assert(sizeof(I) == 0 && sizeof(O) == 0, "should never happen"); diff --git a/test/api/test_in_range.cpp b/test/api/test_in_range.cpp index b22ee5cd9d4e021a70d2450fc136334a4d8f09ea..c2905e1d8289bc7793f9844b30fda6e19d37dd6b 100644 --- a/test/api/test_in_range.cpp +++ b/test/api/test_in_range.cpp @@ -19,8 +19,8 @@ template class InRangeTest final { private: template - static constexpr T min() { - return std::numeric_limits::min(); + static constexpr T lowest() { + return std::numeric_limits::lowest(); } template @@ -67,7 +67,7 @@ class InRangeTest final { const float oneNaN = floatval(0x7FC00001); const float zeroDivZero = -std::numeric_limits::quiet_NaN(); - const float floatMin = std::numeric_limits::min(); + const float floatMin = std::numeric_limits::lowest(); const float floatMax = std::numeric_limits::max(); const float posSubnormalMin = std::numeric_limits::denorm_min(); @@ -98,8 +98,11 @@ class InRangeTest final { template size_t get_linear_height(size_t width, size_t minimum_size) { + // Calling this with a float type would generate a value of infinity. + static_assert(std::is_integral_v); + size_t image_size = - std::max(minimum_size, static_cast(max() - min())); + std::max(minimum_size, static_cast(max() - lowest())); size_t height = image_size / width + 1; return height; @@ -112,15 +115,8 @@ class InRangeTest final { test::Array2D expected(width, height, 1, 1); test::Array2D actual(width, height, 1, 1); - if constexpr (std::is_same_v) { - test::GenerateLinearSeries generator(min()); - source.fill(generator); - } else if constexpr (std::is_same_v) { - test::GenerateLinearSeries generator(min()); - source.fill(generator); - } else { - static_assert(sizeof(T), "should never happen"); - } + test::GenerateLinearSeries generator(0); + source.fill(generator); calculate_expected(source, expected); @@ -149,7 +145,7 @@ class InRangeTest final { {{ { 0, 0, 0, 0}, { 0, 0, 255, 255}, - { 0, 0, 255, 0}, + { 0, 0, 0, 0}, {255, 255, 255, 255}, { 0, 0, 255, 255}, {255, 255, 0, 0}, @@ -286,13 +282,19 @@ class InRangeTest final { return kTestElements; } + static uint8_t increment(uint8_t x) { return x + 1; } + + static float increment(float x) { return std::nextafter(x, FLT_MAX); } + const Elements& get_min_test_elements() { static const Elements kTestElements = { // clang-format off 3, 1, - min(), min() + 1, + lowest(), increment(lowest()), {{ - {min(), min() + 1, min() + 2} + {lowest(), + increment(lowest()), + increment(increment(lowest()))} }}, {{ { 255, 255, 0} diff --git a/test/api/test_saturating_absdiff.cpp b/test/api/test_saturating_absdiff.cpp index 61bc576f1bb328f4efa906d7219e28d0742eb88a..f2aff79f0e2573b6594e81732e895f86b5d27cb5 100644 --- a/test/api/test_saturating_absdiff.cpp +++ b/test/api/test_saturating_absdiff.cpp @@ -26,7 +26,7 @@ class SaturatingAbsDiffTest final : public BinaryOperationTest { protected: using Elements = typename BinaryOperationTest::Elements; - using BinaryOperationTest::min; + using BinaryOperationTest::lowest; using BinaryOperationTest::max; // Calls the API-under-test in the appropriate way. @@ -58,22 +58,22 @@ class SaturatingAbsDiffTest final : public BinaryOperationTest { } else { static const std::vector kTestElements = { // clang-format off - {min(), max(), max()}, - {max(), min(), max()}, - { -1, max(), max()}, - {max(), -1, max()}, - {min(), min() + 1, 1}, - { -1, -2, 1}, - { -2, -1, 1}, - { -1, -1, 0}, - { 0, 0, 0}, - { 1, 1, 0}, - { 2, 1, 1}, - { 1, 2, 1}, - { 0, max(), max()}, - {max(), max(), 0}, - {max(), max() - 1, 1}, - {max(), 0, max()}, + {lowest(), max(), max()}, + { max(), lowest(), max()}, + { -1, max(), max()}, + { max(), -1, max()}, + {lowest(), lowest() + 1, 1}, + { -1, -2, 1}, + { -2, -1, 1}, + { -1, -1, 0}, + { 0, 0, 0}, + { 1, 1, 0}, + { 2, 1, 1}, + { 1, 2, 1}, + { 0, max(), max()}, + { max(), max(), 0}, + { max(), max() - 1, 1}, + { max(), 0, max()}, // clang-format on }; diff --git a/test/api/test_saturating_add.cpp b/test/api/test_saturating_add.cpp index f39b52b12d8e352a728d68a922de2a726b1e2239..c1b77fa648398ae5c4b3117a67af0659216867f0 100644 --- a/test/api/test_saturating_add.cpp +++ b/test/api/test_saturating_add.cpp @@ -29,7 +29,7 @@ class SaturatingAddTest final : public BinaryOperationTest { protected: using Elements = typename BinaryOperationTest::Elements; - using BinaryOperationTest::min; + using BinaryOperationTest::lowest; using BinaryOperationTest::max; // Calls the API-under-test in the appropriate way. @@ -58,15 +58,15 @@ class SaturatingAddTest final : public BinaryOperationTest { } else { static const std::vector kTestElements = { // clang-format off - { min(), min(), min()}, - {min() + 1, -2, min()}, - {min() + 1, -1, min()}, - { -1, -1, -2}, - { 0, 0, 0}, - { 1, 1, 2}, - {max() - 1, 1, max()}, - {max() - 1, 2, max()}, - { max(), max(), max()}, + { lowest(), lowest(), lowest()}, + {lowest() + 1, -2, lowest()}, + {lowest() + 1, -1, lowest()}, + { -1, -1, -2}, + { 0, 0, 0}, + { 1, 1, 2}, + { max() - 1, 1, max()}, + { max() - 1, 2, max()}, + { max(), max(), max()}, // clang-format on }; diff --git a/test/api/test_saturating_multiply.cpp b/test/api/test_saturating_multiply.cpp index c16111c104455a4da77d42d17c163e59f0f088e5..c8216ca308292475e2c63eaf0bd2939a284efe8b 100644 --- a/test/api/test_saturating_multiply.cpp +++ b/test/api/test_saturating_multiply.cpp @@ -26,7 +26,7 @@ class SaturatingMultiplyTest final : public BinaryOperationTest { protected: using Elements = typename BinaryOperationTest::Elements; - using BinaryOperationTest::min; + using BinaryOperationTest::lowest; using BinaryOperationTest::max; // Calls the API-under-test in the appropriate way. @@ -59,10 +59,10 @@ class SaturatingMultiplyTest final : public BinaryOperationTest { } else { static const std::vector kTestElements = { // clang-format off - { min(), min(), max()}, - { min(), max(), min()}, - { min(), -1, max()}, - {min() + 2, -1, max() - 1}, + { lowest(), lowest(), max()}, + { lowest(), max(), lowest()}, + { lowest(), -1, max()}, + {lowest() + 2, -1, max() - 1}, { 6, -8, -48}, { -2, 2, -4}, { -1, -1, 1}, @@ -70,7 +70,7 @@ class SaturatingMultiplyTest final : public BinaryOperationTest { { 1, 1, 1}, { 2, 2, 4}, { 6, 8, 48}, - {max() - 2, -1, min() + 3}, + {max() - 2, -1, lowest() + 3}, { max(), 1, max()}, { max(), 2, max()}, { max(), max(), max()}, diff --git a/test/api/test_saturating_sub.cpp b/test/api/test_saturating_sub.cpp index 09cd365508c9326c197cc7f6900575ccdbb05ab4..43178f0ad04cbf0d3e81cb0338d59d830faa8d54 100644 --- a/test/api/test_saturating_sub.cpp +++ b/test/api/test_saturating_sub.cpp @@ -29,7 +29,7 @@ class SaturatingSubTest final : public BinaryOperationTest { protected: using Elements = typename BinaryOperationTest::Elements; - using BinaryOperationTest::min; + using BinaryOperationTest::lowest; using BinaryOperationTest::max; // Calls the API-under-test in the appropriate way. @@ -58,17 +58,17 @@ class SaturatingSubTest final : public BinaryOperationTest { } else { static const std::vector kTestElements = { // clang-format off - { min(), max(), min()}, - { min(), 1, min()}, - {min() + 1, 2, min()}, - { min(), -1, min() + 1}, - { min(), min(), 0}, - { 0, 0, 0}, - { 2, 1, 1}, - { 1, 1, 0}, - { 1, 2, -1}, - { max(), max(), 0}, - { 2, min(), max()}, + { lowest(), max(), lowest()}, + { lowest(), 1, lowest()}, + {lowest() + 1, 2, lowest()}, + { lowest(), -1, lowest() + 1}, + { lowest(), lowest(), 0}, + { 0, 0, 0}, + { 2, 1, 1}, + { 1, 1, 0}, + { 1, 2, -1}, + { max(), max(), 0}, + { 2, lowest(), max()}, // clang-format on }; diff --git a/test/api/test_scale.cpp b/test/api/test_scale.cpp index 39663a2571881ab681e4fe5255fc8ecfddfd53e3..0b4a20dc09cc904372f8e1d012cae41df240ed8a 100644 --- a/test/api/test_scale.cpp +++ b/test/api/test_scale.cpp @@ -27,8 +27,8 @@ static DestinationType saturating_cast(SourceType value) { uint8_t scalar_scale_u8(uint8_t x, float scale, float shift) { float result = static_cast(x) * scale + shift; - if (result < std::numeric_limits::min()) { - return std::numeric_limits::min(); + if (result < std::numeric_limits::lowest()) { + return std::numeric_limits::lowest(); } if (result > std::numeric_limits::max()) { return std::numeric_limits::max(); @@ -54,7 +54,7 @@ KLEIDICV_SCALE_OPERATION(float, f32); template class ScaleTestBase : public UnaryOperationTest { protected: - using UnaryOperationTest::min; + using UnaryOperationTest::lowest; using UnaryOperationTest::max; using typename UnaryOperationTest::Elements; @@ -104,7 +104,8 @@ class ScaleTestLinearBase { } protected: - static constexpr ElementType min() { + static constexpr ElementType + min_if_integral_else_smallest_positive_normalized() { return std::numeric_limits::min(); } static constexpr ElementType max() { @@ -145,7 +146,8 @@ class ScaleTestLinearBase { test::Array2D actual = test::Array2D(width, height, padding_, 1); - GenerateLinearSeries generator(min(), step); + GenerateLinearSeries generator( + min_if_integral_else_smallest_positive_normalized(), step); source.fill(generator); @@ -270,7 +272,8 @@ class ScaleTestMultiply final : public ScaleTestBase { template class ScaleTestZero final : public ScaleTestBase { using Elements = typename UnaryOperationTest::Elements; - using UnaryOperationTest::min; + using UnaryOperationTest< + ElementType>::min_if_integral_else_smallest_positive_normalized; using UnaryOperationTest::max; using UnaryOperationTest::lowest; @@ -281,7 +284,7 @@ class ScaleTestZero final : public ScaleTestBase { static std::vector kTestElements = { // clang-format off { lowest(), 0}, - { min(), 0}, + { min_if_integral_else_smallest_positive_normalized(), 0}, { 0, 0}, { max(), 0}, // clang-format on @@ -294,7 +297,8 @@ class ScaleTestZero final : public ScaleTestBase { template class ScaleTestUnderflowByShift final : public ScaleTestBase { using Elements = typename UnaryOperationTest::Elements; - using UnaryOperationTest::min; + using UnaryOperationTest< + ElementType>::min_if_integral_else_smallest_positive_normalized; using UnaryOperationTest::lowest; float scale() override { return 1; } @@ -305,8 +309,8 @@ class ScaleTestUnderflowByShift final : public ScaleTestBase { // clang-format off {lowest() + 1, 0}, { lowest(), 0}, - { min() + 1, 0}, - { min(), 0}, + {min_if_integral_else_smallest_positive_normalized() + 1, 0}, + { min_if_integral_else_smallest_positive_normalized(), 0}, { 0, 0}, { 1, 0}, { 2, 0}, @@ -341,7 +345,6 @@ template class ScaleTestUnderflowByScale final : public ScaleTestBase { using Elements = typename UnaryOperationTest::Elements; using UnaryOperationTest::max; - using UnaryOperationTest::min; float scale() override { return -2; } float shift() override { return 0; } diff --git a/test/api/test_thread_min_max.cpp b/test/api/test_thread_min_max.cpp index 83bcb6aa281b2bd47e72cf5d07de6b9bdc790f8e..70537b817c698336228fe117b73aecd85d03a918 100644 --- a/test/api/test_thread_min_max.cpp +++ b/test/api/test_thread_min_max.cpp @@ -33,6 +33,25 @@ KLEIDICV_THREAD_MIN_MAX(uint16_t, u16); KLEIDICV_THREAD_MIN_MAX(int32_t, s32); KLEIDICV_THREAD_MIN_MAX(float, f32); +TEST(MinMaxThread, SimpleFloat) { + size_t width = 10, height = 10; + test::Array2D src(width, height); + + for (auto value : {1.0F, -1.0F}) { + src.fill(value); + + float minval = 1, maxval = -1; + + kleidicv_error_t result = + thread_min_max()(src.data(), src.stride(), width, height, + &minval, &maxval, get_multithreading_fake(3)); + + EXPECT_EQ(KLEIDICV_OK, result); + EXPECT_EQ(value, minval); + EXPECT_EQ(value, maxval); + } +} + template class MinMaxThread : public testing::Test {}; @@ -46,13 +65,13 @@ static const auto test_params = { P{2, 3, 1}, P{6, 4, 1}, P{4, 5, 2}, P{2, 6, 3}, P{1, 7, 4}, P{12, 34, 5}}; TYPED_TEST_P(MinMaxThread, CompareWithSingle) { + test::PseudoRandomNumberGenerator generator; size_t width = 0, height = 0, thread_count = 0; for (auto params : test_params) { std::tie(width, height, thread_count) = params; test::Array2D src(width, height); TypeParam min_single, max_single, min_multi, max_multi; - test::PseudoRandomNumberGenerator generator; src.fill(generator); kleidicv_error_t single_result = min_max()( diff --git a/test/api/test_threshold_binary.cpp b/test/api/test_threshold_binary.cpp index df8e3c2e2c9067d27ec740ded364eecb98422022..df15006225cfe166c5678a928b5df55386c15bb9 100644 --- a/test/api/test_threshold_binary.cpp +++ b/test/api/test_threshold_binary.cpp @@ -53,15 +53,15 @@ template class ThresholdBinaryTestMin final : public ThresholdBinaryTestBase { using Elements = typename UnaryOperationTest::Elements; - using UnaryOperationTest::min; + using UnaryOperationTest::lowest; - ElementType threshold() override { return min(); } + ElementType threshold() override { return lowest(); } const std::vector& test_elements() override { static const std::vector kTestElements = { // clang-format off - { min(), 0}, - {min() + 1, this->value()}, + { lowest(), 0}, + {lowest() + 1, this->value()}, // clang-format on }; return kTestElements; diff --git a/test/framework/generator.h b/test/framework/generator.h index 3870737e324d9895e0135a656aa2cf1e90ef1f9f..4d87e97e805b0256f2bd4b2ea6a2f91aae890830 100644 --- a/test/framework/generator.h +++ b/test/framework/generator.h @@ -5,6 +5,8 @@ #ifndef KLEIDICV_TEST_FRAMEWORK_GENERATOR_H_ #define KLEIDICV_TEST_FRAMEWORK_GENERATOR_H_ +#include +#include #include #include "framework/abstract.h" @@ -35,7 +37,27 @@ class PseudoRandomNumberGenerator : public Generator { // Yields the next value or std::nullopt. std::optional next() override { - return static_cast(rng_()); + if constexpr (std::is_floating_point_v) { + // Return a random floating point value. + // The value is from a uniform distribution of the *representable* + // finite floating point values, not the range. This means that the + // likelihood of producing a tiny value is about the same as the + // likelihood of producing a huge value. + + for (;;) { + ElementType result; + const auto r = rng_(); + static_assert(sizeof(result) <= sizeof(r)); + memcpy(&result, &r, sizeof(result)); + if (std::isfinite(result)) { + return result; + } + } + } else { + return std::uniform_int_distribution( + std::numeric_limits::lowest(), + std::numeric_limits::max())(rng_); + } } protected: diff --git a/test/framework/operation.h b/test/framework/operation.h index 2cc59e8f8bd0d4e790c7773b670992c3d734ff4b..70ecf5cf600637bc1c7f25c66d0dd59c723677eb 100644 --- a/test/framework/operation.h +++ b/test/framework/operation.h @@ -137,8 +137,10 @@ class OperationTest { // Tested number of elements in a row. virtual size_t width() const { return width_; } - // Returns the minimum value for ElementType. - static constexpr ElementType min() { + // Returns the minimum value for integral types, or the minimum normalized + // positive value for floating point types. + static constexpr ElementType + min_if_integral_else_smallest_positive_normalized() { return std::numeric_limits::min(); } diff --git a/test/framework/utils.h b/test/framework/utils.h index 622dc527a2b27d6aca23fa204cf6094566c56862..ba069b09f2dad637f2f97f6b214bae21b7e48127 100644 --- a/test/framework/utils.h +++ b/test/framework/utils.h @@ -180,8 +180,8 @@ T saturating_add(T a, T b) { if constexpr (std::is_unsigned_v) { result = std::numeric_limits::max(); } else { - result = - b < 0 ? std::numeric_limits::min() : std::numeric_limits::max(); + result = b < 0 ? std::numeric_limits::lowest() + : std::numeric_limits::max(); } } @@ -198,7 +198,7 @@ T saturating_mul(T a, T b) { if ((a < 0 && b < 0) || (a > 0 && b > 0)) { result = std::numeric_limits::max(); } else { - result = std::numeric_limits::min(); + result = std::numeric_limits::lowest(); } } }