Merge pull request 'Tests for float16' (#157) from float16_tests into master

Reviewed-on: https://git.madvoxel.net/OpenVulkano/OpenVulkano/pulls/157
Reviewed-by: Georg Hagen <georg.hagen@madvoxel.com>
This commit is contained in:
Vladyslav_Baranovskyi_EXT
2024-11-07 19:15:49 +01:00
2 changed files with 204 additions and 12 deletions

View File

@@ -236,15 +236,22 @@ namespace std
return hash<uint16_t>()(key.m_data);
}
};
}
#endif
#undef __has_keyword
namespace std
{
template <>
class numeric_limits<float16>
{
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
@@ -257,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;
@@ -272,16 +279,12 @@ 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);*/ }
};
}
#endif
#undef __has_keyword
typedef float16 fp16;
typedef float16 half;

189
tests/Math/Float16.cpp Normal file
View File

@@ -0,0 +1,189 @@
/*
* 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 <catch2/catch_all.hpp>
#include <cmath>
#include <limits>
#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<float>(fp16(0.0f)), 0.0f));
REQUIRE(almostEqual(static_cast<float>(fp16(-0.0f)), -0.0f));
REQUIRE(almostEqual(static_cast<float>(fp16(1.0f)), 1.0f));
REQUIRE(almostEqual(static_cast<float>(fp16(-1.0f)), -1.0f));
REQUIRE(almostEqual(static_cast<float>(fp16(0.5f)), 0.5f));
REQUIRE(almostEqual(static_cast<float>(fp16(-0.5f)), -0.5f));
REQUIRE(almostEqual(static_cast<float>(fp16(65504.0f)), 65504.0f));
REQUIRE(almostEqual(static_cast<float>(fp16(-65504.0f)), -65504.0f));
}
TEST_CASE("Infinity and NaN", "[float16]")
{
fp16 neg_inf = -std::numeric_limits<fp16>::infinity();
fp16 min_val = std::numeric_limits<fp16>::min();
fp16 max_val = std::numeric_limits<fp16>::max();
fp16 epsilon_val = std::numeric_limits<fp16>::epsilon();
fp16 round_err_val = std::numeric_limits<fp16>::round_error();
fp16 denorm_min = std::numeric_limits<fp16>::denorm_min();
fp16 pos_inf = std::numeric_limits<fp16>::infinity();
fp16 nan_val = std::numeric_limits<fp16>::quiet_NaN();
fp16 snan_val = std::numeric_limits<fp16>::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<float>(pos_inf));
INFO("Value of neg_inf: " << static_cast<float>(neg_inf));
INFO("Value of nan_val: " << static_cast<float>(nan_val));
INFO("Result of comparison: " << (nan_val != nan_val));
INFO("Value of true pos_inf: " << std::numeric_limits<float>::infinity());
INFO("Value of true neg_inf: " << -std::numeric_limits<float>::infinity());
INFO("Value of true nan: " << std::numeric_limits<float>::quiet_NaN());
auto _inf = std::numeric_limits<float>::infinity();
auto _ninf = -std::numeric_limits<float>::infinity();
auto _nan = std::numeric_limits<float>::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<float>(pos_inf)));
CHECK(std::isinf(static_cast<float>(neg_inf)));
CHECK(std::isnan(static_cast<float>(nan_val)));
CHECK(pos_inf > fp16(65504.0f));
CHECK(neg_inf < fp16(-65504.0f));
}
TEST_CASE("Denormalized Values", "[float16]")
{
fp16 min_denorm = std::numeric_limits<fp16>::denorm_min();
REQUIRE(almostEqual(static_cast<float>(min_denorm), 5.96e-8f));
fp16 denorm_value(1.0e-5f);
REQUIRE(almostEqual(static_cast<float>(denorm_value), 0.0f));
}
TEST_CASE("Special Cases", "[float16]")
{
fp16 zero(0.0f), neg_zero(-0.0f), epsilon_fp(std::numeric_limits<fp16>::epsilon());
fp16 min_normal(std::numeric_limits<fp16>::min());
fp16 max_fp(std::numeric_limits<fp16>::max());
INFO("Value of max_fp: " << static_cast<float>(max_fp));
CHECK(almostEqual(static_cast<float>(zero), 0.0f));
CHECK(almostEqual(static_cast<float>(neg_zero), 0.0f));
CHECK(almostEqual(static_cast<float>(epsilon_fp), 0.00097656f));
CHECK(almostEqual(static_cast<float>(min_normal), 6.10352e-5f));
CHECK(almostEqual(static_cast<float>(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<float>(a + b), 3.0f));
REQUIRE(almostEqual(static_cast<float>(b - a), 1.0f));
REQUIRE(almostEqual(static_cast<float>(a * b), 2.0f));
REQUIRE(almostEqual(static_cast<float>(d / c), 1.33333f));
REQUIRE(almostEqual(static_cast<float>(large * small), 32752.0f));
REQUIRE(std::isinf(static_cast<float>(large + large)));
REQUIRE(almostEqual(static_cast<float>(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<fp16>::infinity();
INFO("Value of inf: " << static_cast<float>(inf));
INFO("Value of zero: " << static_cast<float>(zero));
INFO("Value of neg: " << static_cast<float>(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<fp16>::infinity(), nan = std::numeric_limits<fp16>::quiet_NaN();
INFO("Value of a: " << static_cast<float>(a));
INFO("Value of inf: " << static_cast<float>(inf));
INFO("Value of nan: " << static_cast<float>(nan));
REQUIRE(std::isinf(static_cast<float>(a + inf)));
REQUIRE(std::isinf(static_cast<float>(inf - a)));
REQUIRE(std::isnan(static_cast<float>(inf - inf)));
REQUIRE(std::isnan(static_cast<float>(inf / inf)));
REQUIRE(std::isnan(static_cast<float>(nan + a)));
REQUIRE(std::isnan(static_cast<float>(nan - a)));
REQUIRE(std::isnan(static_cast<float>(a * nan)));
REQUIRE(std::isnan(static_cast<float>(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<float>(near_min), 6.10352e-5f));
REQUIRE(almostEqual(static_cast<float>(near_max), 65504.0f));
REQUIRE(almostEqual(static_cast<float>(tiny_pos), 0.0f));
REQUIRE(almostEqual(static_cast<float>(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<float>(max_convertible_fp), 65504.0f));
REQUIRE(std::isinf(static_cast<float>(too_large)));
REQUIRE(almostEqual(static_cast<float>(too_small), 0.0f));
}
TEST_CASE("Special Comparison with Infinity and NaN", "[float16]")
{
fp16 inf = std::numeric_limits<fp16>::infinity();
fp16 neg_inf = -std::numeric_limits<fp16>::infinity();
fp16 nan = std::numeric_limits<fp16>::quiet_NaN();
fp16 one(1.0f);
INFO("Value of one: " << static_cast<float>(one));
INFO("Value of inf: " << static_cast<float>(inf));
INFO("Value of neg_inf: " << static_cast<float>(neg_inf));
INFO("Value of nan: " << static_cast<float>(nan));
CHECK(inf > one);
CHECK(neg_inf < -one);
CHECK(std::isnan(static_cast<float>(nan)));
CHECK(nan != nan);
}