From 8066b2d0599b994daf17141a31b82f4c2a866a70 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Tue, 5 Nov 2024 21:42:39 +0200 Subject: [PATCH 1/7] Tests for float16 --- tests/Math/Float16.cpp | 139 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 tests/Math/Float16.cpp diff --git a/tests/Math/Float16.cpp b/tests/Math/Float16.cpp new file mode 100644 index 0000000..a5f750d --- /dev/null +++ b/tests/Math/Float16.cpp @@ -0,0 +1,139 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include + +#include +#include + +#include "Math/Float16.hpp" + +// using namespace OpenVulkano::Math; + +namespace +{ + bool almostEqual(float a, float b, float epsilon = 0.001f) { return std::fabs(a - b) < epsilon; } +} + +TEST_CASE("Basic Conversions", "[float16]") +{ + REQUIRE(almostEqual(static_cast(fp16(0.0f)), 0.0f)); + REQUIRE(almostEqual(static_cast(fp16(-0.0f)), -0.0f)); + REQUIRE(almostEqual(static_cast(fp16(1.0f)), 1.0f)); + REQUIRE(almostEqual(static_cast(fp16(-1.0f)), -1.0f)); + REQUIRE(almostEqual(static_cast(fp16(0.5f)), 0.5f)); + REQUIRE(almostEqual(static_cast(fp16(-0.5f)), -0.5f)); + REQUIRE(almostEqual(static_cast(fp16(65504.0f)), 65504.0f)); + REQUIRE(almostEqual(static_cast(fp16(-65504.0f)), -65504.0f)); +} + +TEST_CASE("Infinity and NaN", "[float16]") +{ + fp16 pos_inf = std::numeric_limits::infinity(); + fp16 neg_inf = -std::numeric_limits::infinity(); + fp16 nan_val = std::numeric_limits::quiet_NaN(); + + REQUIRE(std::isinf(static_cast(pos_inf))); + REQUIRE(std::isinf(static_cast(neg_inf))); + REQUIRE(std::isnan(static_cast(nan_val))); + REQUIRE(pos_inf > fp16(65504.0f)); + REQUIRE(neg_inf < fp16(-65504.0f)); +} + +TEST_CASE("Denormalized Values", "[float16]") +{ + fp16 min_denorm = std::numeric_limits::denorm_min(); + REQUIRE(almostEqual(static_cast(min_denorm), 5.96e-8f)); + + fp16 denorm_value(1.0e-5f); + REQUIRE(almostEqual(static_cast(denorm_value), 0.0f)); +} + +TEST_CASE("Special Cases", "[float16]") +{ + fp16 zero(0.0f), neg_zero(-0.0f), epsilon_fp(std::numeric_limits::epsilon()); + fp16 min_normal(std::numeric_limits::min()); + fp16 max_fp(std::numeric_limits::max()); + + REQUIRE(almostEqual(static_cast(zero), 0.0f)); + REQUIRE(almostEqual(static_cast(neg_zero), 0.0f)); + REQUIRE(almostEqual(static_cast(epsilon_fp), 0.00097656f)); + REQUIRE(almostEqual(static_cast(min_normal), 6.10352e-5f)); + REQUIRE(almostEqual(static_cast(max_fp), 65504.0f)); +} + +TEST_CASE("Arithmetic Operations", "[float16]") +{ + fp16 a(1.0f), b(2.0f), c(3.0f), d(4.0f), large(65504.0f), small(0.5f); + + REQUIRE(almostEqual(static_cast(a + b), 3.0f)); + REQUIRE(almostEqual(static_cast(b - a), 1.0f)); + REQUIRE(almostEqual(static_cast(a * b), 2.0f)); + REQUIRE(almostEqual(static_cast(d / c), 1.33333f)); + REQUIRE(almostEqual(static_cast(large * small), 32752.0f)); + + REQUIRE(std::isinf(static_cast(large + large))); + REQUIRE(almostEqual(static_cast(a - a), 0.0f)); +} + +TEST_CASE("Comparisons", "[float16]") +{ + fp16 a(1.0f), b(2.0f), zero(0.0f), neg(-1.0f), inf = std::numeric_limits::infinity(); + + REQUIRE(a < b); + REQUIRE(b > a); + REQUIRE(zero == -zero); + REQUIRE(a != neg); + REQUIRE(inf > b); + REQUIRE(!(inf < b)); +} + +TEST_CASE("Arithmetic with Special Values", "[float16]") +{ + fp16 a(2.0f), inf = std::numeric_limits::infinity(), nan = std::numeric_limits::quiet_NaN(); + + REQUIRE(std::isinf(static_cast(a + inf))); + REQUIRE(std::isinf(static_cast(inf - a))); + REQUIRE(std::isnan(static_cast(inf - inf))); + REQUIRE(std::isnan(static_cast(inf / inf))); + + REQUIRE(std::isnan(static_cast(nan + a))); + REQUIRE(std::isnan(static_cast(nan - a))); + REQUIRE(std::isnan(static_cast(a * nan))); + REQUIRE(std::isnan(static_cast(a / nan))); +} + +TEST_CASE("Boundary Tests", "[float16]") +{ + fp16 near_min(6.10352e-5f), near_max(65504.0f), tiny_pos(1.0e-8f), tiny_neg(-1.0e-8f); + + REQUIRE(almostEqual(static_cast(near_min), 6.10352e-5f)); + REQUIRE(almostEqual(static_cast(near_max), 65504.0f)); + REQUIRE(almostEqual(static_cast(tiny_pos), 0.0f)); + REQUIRE(almostEqual(static_cast(tiny_neg), 0.0f)); +} + +TEST_CASE("Edge Case Conversions", "[float16]") +{ + fp16 max_convertible_fp(65504.0f), too_large(70000.0f), too_small(1.0e-8f); + + REQUIRE(almostEqual(static_cast(max_convertible_fp), 65504.0f)); + REQUIRE(std::isinf(static_cast(too_large))); + REQUIRE(almostEqual(static_cast(too_small), 0.0f)); +} + +TEST_CASE("Special Comparison with Infinity and NaN", "[float16]") +{ + fp16 inf = std::numeric_limits::infinity(); + fp16 neg_inf = -std::numeric_limits::infinity(); + fp16 nan = std::numeric_limits::quiet_NaN(); + fp16 one(1.0f); + + REQUIRE(inf > one); + REQUIRE(neg_inf < -one); + REQUIRE(std::isnan(static_cast(nan))); + REQUIRE(nan != nan); +} \ No newline at end of file From ebc4b696b0f7b4316b1dc5d28a07998096f398fb Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Thu, 7 Nov 2024 13:50:13 +0200 Subject: [PATCH 2/7] Added debug info for failing tests --- tests/Math/Float16.cpp | 59 ++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/tests/Math/Float16.cpp b/tests/Math/Float16.cpp index a5f750d..4559ca4 100644 --- a/tests/Math/Float16.cpp +++ b/tests/Math/Float16.cpp @@ -36,11 +36,15 @@ TEST_CASE("Infinity and NaN", "[float16]") fp16 neg_inf = -std::numeric_limits::infinity(); fp16 nan_val = std::numeric_limits::quiet_NaN(); - REQUIRE(std::isinf(static_cast(pos_inf))); - REQUIRE(std::isinf(static_cast(neg_inf))); - REQUIRE(std::isnan(static_cast(nan_val))); - REQUIRE(pos_inf > fp16(65504.0f)); - REQUIRE(neg_inf < fp16(-65504.0f)); + INFO("Value of pos_inf: " << static_cast(pos_inf)); + INFO("Value of nan_val: " << static_cast(nan_val)); + INFO("Result of comparison: " << (nan_val != nan_val)); + + CHECK(std::isinf(static_cast(pos_inf))); + CHECK(std::isinf(static_cast(neg_inf))); + CHECK(std::isnan(static_cast(nan_val))); + CHECK(pos_inf > fp16(65504.0f)); + CHECK(neg_inf < fp16(-65504.0f)); } TEST_CASE("Denormalized Values", "[float16]") @@ -58,11 +62,13 @@ TEST_CASE("Special Cases", "[float16]") fp16 min_normal(std::numeric_limits::min()); fp16 max_fp(std::numeric_limits::max()); - REQUIRE(almostEqual(static_cast(zero), 0.0f)); - REQUIRE(almostEqual(static_cast(neg_zero), 0.0f)); - REQUIRE(almostEqual(static_cast(epsilon_fp), 0.00097656f)); - REQUIRE(almostEqual(static_cast(min_normal), 6.10352e-5f)); - REQUIRE(almostEqual(static_cast(max_fp), 65504.0f)); + INFO("Value of max_fp: " << static_cast(max_fp)); + + CHECK(almostEqual(static_cast(zero), 0.0f)); + CHECK(almostEqual(static_cast(neg_zero), 0.0f)); + CHECK(almostEqual(static_cast(epsilon_fp), 0.00097656f)); + CHECK(almostEqual(static_cast(min_normal), 6.10352e-5f)); + CHECK(almostEqual(static_cast(max_fp), 65504.0f)); } TEST_CASE("Arithmetic Operations", "[float16]") @@ -83,18 +89,26 @@ TEST_CASE("Comparisons", "[float16]") { fp16 a(1.0f), b(2.0f), zero(0.0f), neg(-1.0f), inf = std::numeric_limits::infinity(); - REQUIRE(a < b); - REQUIRE(b > a); - REQUIRE(zero == -zero); - REQUIRE(a != neg); - REQUIRE(inf > b); - REQUIRE(!(inf < b)); + INFO("Value of inf: " << static_cast(inf)); + INFO("Value of zero: " << static_cast(zero)); + INFO("Value of neg: " << static_cast(neg)); + + CHECK(a < b); + CHECK(b > a); + CHECK(zero == -zero); + CHECK(a != neg); + CHECK(inf > b); + CHECK(!(inf < b)); } TEST_CASE("Arithmetic with Special Values", "[float16]") { fp16 a(2.0f), inf = std::numeric_limits::infinity(), nan = std::numeric_limits::quiet_NaN(); + INFO("Value of a: " << static_cast(a)); + INFO("Value of inf: " << static_cast(inf)); + INFO("Value of nan: " << static_cast(nan)); + REQUIRE(std::isinf(static_cast(a + inf))); REQUIRE(std::isinf(static_cast(inf - a))); REQUIRE(std::isnan(static_cast(inf - inf))); @@ -132,8 +146,13 @@ TEST_CASE("Special Comparison with Infinity and NaN", "[float16]") fp16 nan = std::numeric_limits::quiet_NaN(); fp16 one(1.0f); - REQUIRE(inf > one); - REQUIRE(neg_inf < -one); - REQUIRE(std::isnan(static_cast(nan))); - REQUIRE(nan != nan); + INFO("Value of one: " << static_cast(one)); + INFO("Value of inf: " << static_cast(inf)); + INFO("Value of neg_inf: " << static_cast(neg_inf)); + INFO("Value of nan: " << static_cast(nan)); + + CHECK(inf > one); + CHECK(neg_inf < -one); + CHECK(std::isnan(static_cast(nan))); + CHECK(nan != nan); } \ No newline at end of file From e9c19b42da2dfb83fbb7ea79a9decfc133ac675c Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Thu, 7 Nov 2024 15:41:14 +0200 Subject: [PATCH 3/7] Added more info to Float16 tests --- tests/Math/Float16.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/Math/Float16.cpp b/tests/Math/Float16.cpp index 4559ca4..22093a8 100644 --- a/tests/Math/Float16.cpp +++ b/tests/Math/Float16.cpp @@ -37,9 +37,22 @@ TEST_CASE("Infinity and NaN", "[float16]") fp16 nan_val = std::numeric_limits::quiet_NaN(); INFO("Value of pos_inf: " << static_cast(pos_inf)); + INFO("Value of neg_inf: " << static_cast(neg_inf)); INFO("Value of nan_val: " << static_cast(nan_val)); INFO("Result of comparison: " << (nan_val != nan_val)); + INFO("Value of true pos_inf: " << std::numeric_limits::infinity()); + INFO("Value of true neg_inf: " << -std::numeric_limits::infinity()); + INFO("Value of true nan: " << std::numeric_limits::quiet_NaN()); + + auto _inf = std::numeric_limits::infinity(); + auto _ninf = -std::numeric_limits::infinity(); + auto _nan = std::numeric_limits::quiet_NaN(); + + INFO("Value of true pos_inf: " << std::hex << *(uint32_t *)&_inf); + INFO("Value of true neg_inf: " << std::hex << *(uint32_t *)&_ninf); + INFO("Value of true nan: " << std::hex << *(uint32_t *)&_nan); + CHECK(std::isinf(static_cast(pos_inf))); CHECK(std::isinf(static_cast(neg_inf))); CHECK(std::isnan(static_cast(nan_val))); From 649d8a07f3887fc715e565df9c9042107ef86141 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Thu, 7 Nov 2024 16:01:46 +0200 Subject: [PATCH 4/7] Specializing numeric_limits for float16 in any case --- openVulkanoCpp/Math/Float16.hpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openVulkanoCpp/Math/Float16.hpp b/openVulkanoCpp/Math/Float16.hpp index d013a16..7cc2fae 100644 --- a/openVulkanoCpp/Math/Float16.hpp +++ b/openVulkanoCpp/Math/Float16.hpp @@ -236,7 +236,14 @@ namespace std return hash()(key.m_data); } }; +} +#endif +#undef __has_keyword + + +namespace std +{ template <> class numeric_limits { @@ -279,9 +286,5 @@ namespace std }; } -#endif -#undef __has_keyword - - typedef float16 fp16; typedef float16 half; From a3d365b97734d341e7153aca3833beb91ec48998 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Thu, 7 Nov 2024 16:20:44 +0200 Subject: [PATCH 5/7] Revert "Specializing numeric_limits for float16 in any case" This reverts commit 649d8a07f3887fc715e565df9c9042107ef86141. --- openVulkanoCpp/Math/Float16.hpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/openVulkanoCpp/Math/Float16.hpp b/openVulkanoCpp/Math/Float16.hpp index 7cc2fae..d013a16 100644 --- a/openVulkanoCpp/Math/Float16.hpp +++ b/openVulkanoCpp/Math/Float16.hpp @@ -236,14 +236,7 @@ namespace std return hash()(key.m_data); } }; -} -#endif -#undef __has_keyword - - -namespace std -{ template <> class numeric_limits { @@ -286,5 +279,9 @@ namespace std }; } +#endif +#undef __has_keyword + + typedef float16 fp16; typedef float16 half; From f883d58f14c92425d54c60364d2f24c58ba9c714 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Thu, 7 Nov 2024 16:28:29 +0200 Subject: [PATCH 6/7] Revert "Revert "Specializing numeric_limits for float16 in any case"" This reverts commit a3d365b97734d341e7153aca3833beb91ec48998. --- openVulkanoCpp/Math/Float16.hpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openVulkanoCpp/Math/Float16.hpp b/openVulkanoCpp/Math/Float16.hpp index d013a16..7cc2fae 100644 --- a/openVulkanoCpp/Math/Float16.hpp +++ b/openVulkanoCpp/Math/Float16.hpp @@ -236,7 +236,14 @@ namespace std return hash()(key.m_data); } }; +} +#endif +#undef __has_keyword + + +namespace std +{ template <> class numeric_limits { @@ -279,9 +286,5 @@ namespace std }; } -#endif -#undef __has_keyword - - typedef float16 fp16; typedef float16 half; From f72ceee298e1c5c81a8fb60537aa2e9be891ab14 Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Thu, 7 Nov 2024 16:45:17 +0200 Subject: [PATCH 7/7] Hardcoding numeric_limits values to work on platforms where our float16 class isn't defined --- openVulkanoCpp/Math/Float16.hpp | 16 ++++++++-------- tests/Math/Float16.cpp | 20 +++++++++++++++++++- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/openVulkanoCpp/Math/Float16.hpp b/openVulkanoCpp/Math/Float16.hpp index 7cc2fae..160e6f4 100644 --- a/openVulkanoCpp/Math/Float16.hpp +++ b/openVulkanoCpp/Math/Float16.hpp @@ -250,8 +250,8 @@ namespace std public: // General -- meaningful for all specializations. static const bool is_specialized = true; - static float16 min() { return float16(0, 1, 0); } - static float16 max() { return float16(~0, 30, 0); } + static float16 min() { uint16_t v = 0x400; return *(float16 *)&v;/*return float16(0, 1, 0);*/ } + static float16 max() { uint16_t v = 0x7bff; return *(float16 *)&v;/*return float16(~0, 30, 0);*/ } static const int radix = 2; static const int digits = 10; // conservative assumption static const int digits10 = 2; // conservative assumption @@ -264,8 +264,8 @@ namespace std // Floating point specific. - static float16 epsilon() { return float16(0.00097656f); } // from OpenEXR, needs to be confirmed - static float16 round_error() { return float16(0.00097656f/2); } + static float16 epsilon() { uint16_t v = 0x13ff; return *(float16 *)&v;/*return float16(0.00097656f);*/ } // from OpenEXR, needs to be confirmed + static float16 round_error() { uint16_t v = 0xfff; return *(float16 *)&v;/*return float16(0.00097656f/2);*/ } static const int min_exponent10 = -9; static const int max_exponent10 = 9; static const int min_exponent = -15; @@ -279,10 +279,10 @@ namespace std static const bool tinyness_before = false; static const float_round_style round_style = round_to_nearest; - static float16 denorm_min() { return float16(1, 0, 1); } - static float16 infinity() { return float16(0, 31, 0); } - static float16 quiet_NaN() { return float16(1, 31, 0); } - static float16 signaling_NaN () { return float16(1, 31, 0); } + static float16 denorm_min() { uint16_t v = 0x8001; return *(float16 *)&v;/*return float16(1, 0, 1);*/ } + static float16 infinity() { uint16_t v = 0x7c00; return *(float16 *)&v;/*return float16(0, 31, 0);*/ } + static float16 quiet_NaN() { uint16_t v = 0x7c01; return *(float16 *)&v;/*return float16(1, 31, 0);*/ } + static float16 signaling_NaN () { uint16_t v = 0x7c01; return *(float16 *)&v;/*return float16(1, 31, 0);*/ } }; } diff --git a/tests/Math/Float16.cpp b/tests/Math/Float16.cpp index 22093a8..a4ebb34 100644 --- a/tests/Math/Float16.cpp +++ b/tests/Math/Float16.cpp @@ -32,9 +32,27 @@ TEST_CASE("Basic Conversions", "[float16]") TEST_CASE("Infinity and NaN", "[float16]") { - fp16 pos_inf = std::numeric_limits::infinity(); fp16 neg_inf = -std::numeric_limits::infinity(); + + fp16 min_val = std::numeric_limits::min(); + fp16 max_val = std::numeric_limits::max(); + fp16 epsilon_val = std::numeric_limits::epsilon(); + fp16 round_err_val = std::numeric_limits::round_error(); + fp16 denorm_min = std::numeric_limits::denorm_min(); + fp16 pos_inf = std::numeric_limits::infinity(); fp16 nan_val = std::numeric_limits::quiet_NaN(); + fp16 snan_val = std::numeric_limits::signaling_NaN(); + + INFO("Value of 16 min: " << std::hex << *(uint16_t *)&min_val); + INFO("Value of 16 max: " << std::hex << *(uint16_t *)&max_val); + INFO("Value of 16 epsilon: " << std::hex << *(uint16_t *)&epsilon_val); + INFO("Value of 16 round_error: " << std::hex << *(uint16_t *)&round_err_val); + + INFO("Value of 16 denorm_min: " << std::hex << *(uint16_t *)&denorm_min); + INFO("Value of 16 pos_inf: " << std::hex << *(uint16_t *)&pos_inf); + INFO("Value of 16 neg_inf: " << std::hex << *(uint16_t *)&neg_inf); + INFO("Value of 16 nan: " << std::hex << *(uint16_t *)&nan_val); + INFO("Value of 16 snan: " << std::hex << *(uint16_t *)&snan_val); INFO("Value of pos_inf: " << static_cast(pos_inf)); INFO("Value of neg_inf: " << static_cast(neg_inf));