HalfFloatTests.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include "UnitTestFramework.h"
  4. #include <Jolt/Math/HalfFloat.h>
  5. #include <Jolt/Core/FPException.h>
  6. TEST_SUITE("HalfFloatTests")
  7. {
  8. // Helper function to construct a float with a specific bit pattern
  9. static inline float ReinterpretAsFloat(uint32 inValue)
  10. {
  11. static_assert(sizeof(float) == sizeof(uint32));
  12. union IntToFloat
  13. {
  14. uint32 i;
  15. float f;
  16. };
  17. IntToFloat i_to_f;
  18. i_to_f.i = inValue;
  19. return i_to_f.f;
  20. }
  21. #if defined(JPH_USE_F16C) || defined(JPH_USE_NEON)
  22. TEST_CASE("TestHalfFloatToFloat")
  23. {
  24. // Check all half float values, 4 at a time, skip NaN's and INF
  25. for (uint32 v = 0; v < 0x7c00; v += 2)
  26. {
  27. // Test value, next value and negative variants of both
  28. UVec4 half_float(v | ((v + 1) << 16), (v | 0x8000) | (((v + 1) | 0x8000) << 16), 0, 0);
  29. // Compare hardware intrinsic version with fallback version
  30. Vec4 flt1 = HalfFloatConversion::ToFloat(half_float);
  31. Vec4 flt2 = HalfFloatConversion::ToFloatFallback(half_float);
  32. UVec4 flt1_as_int = flt1.ReinterpretAsInt();
  33. UVec4 flt2_as_int = flt2.ReinterpretAsInt();
  34. if (flt1_as_int != flt2_as_int)
  35. CHECK(false); // Not using CHECK(flt1_as_int == flt2_as_int) macros as that makes the test very slow
  36. }
  37. }
  38. // Helper function to compare the intrinsics version with the fallback version
  39. static inline void CheckFloatToHalfFloat(uint32 inValue, uint32 inSign)
  40. {
  41. const float fvalue = ReinterpretAsFloat(inValue + inSign * 0x80000000U);
  42. HalfFloat hf1 = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEAREST>(fvalue);
  43. HalfFloat hf2 = HalfFloatConversion::FromFloatFallback<HalfFloatConversion::ROUND_TO_NEAREST>(fvalue);
  44. bool result = (hf1 == hf2);
  45. if (!result)
  46. CHECK(false); // Not using CHECK(hf1 == hf2) macros as that makes the test very slow
  47. hf1 = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(fvalue);
  48. hf2 = HalfFloatConversion::FromFloatFallback<HalfFloatConversion::ROUND_TO_POS_INF>(fvalue);
  49. result = (hf1 == hf2);
  50. if (!result)
  51. CHECK(false);
  52. hf1 = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(fvalue);
  53. hf2 = HalfFloatConversion::FromFloatFallback<HalfFloatConversion::ROUND_TO_NEG_INF>(fvalue);
  54. result = (hf1 == hf2);
  55. if (!result)
  56. CHECK(false);
  57. }
  58. TEST_CASE("TestFloatToHalfFloat")
  59. {
  60. for (uint32 sign = 0; sign < 2; ++sign)
  61. {
  62. // Zero and smallest possible float
  63. for (uint32 value = 0; value < 2; value++)
  64. CheckFloatToHalfFloat(value, sign);
  65. // Floats that are large enough to become a denormalized half float, incrementing by smallest increment that can make a difference
  66. for (uint32 value = (HalfFloatConversion::FLOAT_EXPONENT_BIAS - HalfFloatConversion::HALF_FLT_EXPONENT_BIAS - HalfFloatConversion::HALF_FLT_MANTISSA_BITS) << HalfFloatConversion::FLOAT_EXPONENT_POS; value < HalfFloatConversion::FLOAT_EXPONENT_MASK << HalfFloatConversion::FLOAT_EXPONENT_POS; value += 1 << (HalfFloatConversion::FLOAT_MANTISSA_BITS - HalfFloatConversion::HALF_FLT_MANTISSA_BITS - 2))
  67. CheckFloatToHalfFloat(value, sign);
  68. // INF
  69. CheckFloatToHalfFloat(0x7f800000U, sign);
  70. // Nan
  71. CheckFloatToHalfFloat(0x7fc00000U, sign);
  72. }
  73. }
  74. #endif
  75. TEST_CASE("TestHalfFloatINF")
  76. {
  77. // Float -> half float
  78. CHECK(HalfFloatConversion::FromFloatFallback<HalfFloatConversion::ROUND_TO_NEAREST>(ReinterpretAsFloat(0x7f800000U)) == HALF_FLT_INF);
  79. CHECK(HalfFloatConversion::FromFloatFallback<HalfFloatConversion::ROUND_TO_NEAREST>(ReinterpretAsFloat(0xff800000U)) == HALF_FLT_INF_NEGATIVE);
  80. // Half float -> float
  81. UVec4 half_float(uint32(HALF_FLT_INF) | (uint32(HALF_FLT_INF_NEGATIVE) << 16), 0, 0, 0);
  82. UVec4 flt = HalfFloatConversion::ToFloatFallback(half_float).ReinterpretAsInt();
  83. CHECK(flt == UVec4(0x7f800000U, 0xff800000U, 0, 0));
  84. }
  85. TEST_CASE("TestHalfFloatNaN")
  86. {
  87. // Float -> half float
  88. CHECK(HalfFloatConversion::FromFloatFallback<HalfFloatConversion::ROUND_TO_NEAREST>(ReinterpretAsFloat(0x7fc00000U)) == HALF_FLT_NANQ);
  89. CHECK(HalfFloatConversion::FromFloatFallback<HalfFloatConversion::ROUND_TO_NEAREST>(ReinterpretAsFloat(0xffc00000U)) == HALF_FLT_NANQ_NEGATIVE);
  90. // Half float -> float
  91. UVec4 half_float(uint32(HALF_FLT_NANQ) | (uint32(HALF_FLT_NANQ_NEGATIVE) << 16), 0, 0, 0);
  92. UVec4 flt = HalfFloatConversion::ToFloatFallback(half_float).ReinterpretAsInt();
  93. CHECK(flt == UVec4(0x7fc00000U, 0xffc00000U, 0, 0));
  94. }
  95. }