// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #pragma once #include "Math.h" /// /// Half-precision 16 bit floating point number consisting of a sign bit, a 5 bit biased exponent, and a 10 bit mantissa /// typedef uint16 Half; #define USE_SSE_HALF_CONVERSION 0 class Float16Compressor { union Bits { float f; int32 si; uint32 ui; }; static const int shift = 13; static const int shiftSign = 16; static const int32 infN = 0x7F800000; // flt32 infinity static const int32 maxN = 0x477FE000; // max flt16 normal as a flt32 static const int32 minN = 0x38800000; // min flt16 normal as a flt32 static const int32 signN = 0x80000000; // flt32 sign bit static const int32 infC = infN >> shift; static const int32 nanN = (infC + 1) << shift; // minimum flt16 nan as a flt32 static const int32 maxC = maxN >> shift; static const int32 minC = minN >> shift; static const int32 signC = signN >> shiftSign; // flt16 sign bit static const int32 mulN = 0x52000000; // (1 << 23) / minN static const int32 mulC = 0x33800000; // minN / (1 << (23 - shift)) static const int32 subC = 0x003FF; // max flt32 subnormal down shifted static const int32 norC = 0x00400; // min flt32 normal down shifted static const int32 maxD = infC - maxC - 1; static const int32 minD = minC - subC - 1; public: static Half Compress(const float value) { #if USE_SSE_HALF_CONVERSION __m128 V1 = _mm_set_ss(value); __m128i V2 = _mm_cvtps_ph(V1, 0); return static_cast(_mm_cvtsi128_si32(V2)); #else Bits v, s; v.f = value; uint32 sign = v.si & signN; v.si ^= sign; sign >>= shiftSign; // logical shift s.si = mulN; s.si = static_cast(s.f * v.f); // correct subnormals v.si ^= (s.si ^ v.si) & -(minN > v.si); v.si ^= (infN ^ v.si) & -((infN > v.si) & (v.si > maxN)); v.si ^= (nanN ^ v.si) & -((nanN > v.si) & (v.si > infN)); v.ui >>= shift; // logical shift v.si ^= ((v.si - maxD) ^ v.si) & -(v.si > maxC); v.si ^= ((v.si - minD) ^ v.si) & -(v.si > subC); return v.ui | sign; #endif } static float Decompress(const Half value) { #if USE_SSE_HALF_CONVERSION __m128i V1 = _mm_cvtsi32_si128(static_cast(value)); __m128 V2 = _mm_cvtph_ps(V1); return _mm_cvtss_f32(V2); #else Bits v; v.ui = value; int32 sign = v.si & signC; v.si ^= sign; sign <<= shiftSign; v.si ^= ((v.si + minD) ^ v.si) & -(v.si > subC); v.si ^= ((v.si + maxD) ^ v.si) & -(v.si > maxC); Bits s; s.si = mulC; s.f *= v.si; const int32 mask = -(norC > v.si); v.si <<= shift; v.si ^= (s.si ^ v.si) & mask; v.si |= sign; return v.f; #endif } }; inline float ConvertHalfToFloat(const Half value) { return Float16Compressor::Decompress(value); } inline Half ConvertFloatToHalf(const float value) { return Float16Compressor::Compress(value); } /// /// Defines a two component vector, using half precision floating point coordinates. /// struct Half2 { public: /// /// Zero vector /// static Half2 Zero; public: /// /// Gets or sets the X component of the vector. /// Half X; /// /// Gets or sets the Y component of the vector. /// Half Y; public: /// /// Default constructor /// Half2() { } /// /// Init /// /// X component /// Y component Half2(float x, float y) { X = ConvertFloatToHalf(x); Y = ConvertFloatToHalf(y); } /// /// Init /// /// X and Y components Half2(const Vector2& v); public: /// /// Convert to Vector2 /// /// Vector2 Vector2 ToVector2() const; }; /// /// Defines a three component vector, using half precision floating point coordinates. /// struct Half3 { public: /// /// Zero vector /// static Half3 Zero; public: /// /// Gets or sets the X component of the vector. /// Half X; /// /// Gets or sets the Y component of the vector. /// Half Y; /// /// Gets or sets the Z component of the vector. /// Half Z; public: Half3() { } Half3(const float x, const float y, const float z) { X = ConvertFloatToHalf(x); Y = ConvertFloatToHalf(y); Z = ConvertFloatToHalf(z); } Half3(const Vector3& v); public: Vector3 ToVector3() const; }; /// /// Defines a four component vector, using half precision floating point coordinates. /// struct Half4 { public: /// /// Zero vector /// static Half4 Zero; public: /// /// Gets or sets the X component of the vector. /// Half X; /// /// Gets or sets the Y component of the vector. /// Half Y; /// /// Gets or sets the Z component of the vector. /// Half Z; /// /// Gets or sets the W component of the vector. /// Half W; public: Half4() { } Half4(const float x, const float y, const float z) { X = ConvertFloatToHalf(x); Y = ConvertFloatToHalf(y); Z = ConvertFloatToHalf(z); W = 0; } Half4(const float x, const float y, const float z, const float w) { X = ConvertFloatToHalf(x); Y = ConvertFloatToHalf(y); Z = ConvertFloatToHalf(z); W = ConvertFloatToHalf(w); } explicit Half4(const Vector4& v); explicit Half4(const Color& c); explicit Half4(const Rectangle& rect); public: Vector2 ToVector2() const; Vector3 ToVector3() const; Vector4 ToVector4() const; };