Jolt Physics
A multi core friendly Game Physics Engine
Loading...
Searching...
No Matches
HalfFloat.h
Go to the documentation of this file.
1// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
7#include <Jolt/Math/Vec4.h>
8
10
12
13// Define half float constant values
14static constexpr HalfFloat HALF_FLT_MAX = 0x7bff;
15static constexpr HalfFloat HALF_FLT_MAX_NEGATIVE = 0xfbff;
16static constexpr HalfFloat HALF_FLT_INF = 0x7c00;
17static constexpr HalfFloat HALF_FLT_INF_NEGATIVE = 0xfc00;
18static constexpr HalfFloat HALF_FLT_NANQ = 0x7e00;
19static constexpr HalfFloat HALF_FLT_NANQ_NEGATIVE = 0xfe00;
20
22
23// Layout of a float
24static constexpr int FLOAT_SIGN_POS = 31;
25static constexpr int FLOAT_EXPONENT_POS = 23;
26static constexpr int FLOAT_EXPONENT_BITS = 8;
27static constexpr int FLOAT_EXPONENT_MASK = (1 << FLOAT_EXPONENT_BITS) - 1;
28static constexpr int FLOAT_EXPONENT_BIAS = 127;
29static constexpr int FLOAT_MANTISSA_BITS = 23;
30static constexpr int FLOAT_MANTISSA_MASK = (1 << FLOAT_MANTISSA_BITS) - 1;
31static constexpr int FLOAT_EXPONENT_AND_MANTISSA_MASK = FLOAT_MANTISSA_MASK + (FLOAT_EXPONENT_MASK << FLOAT_EXPONENT_POS);
32
33// Layout of half float
34static constexpr int HALF_FLT_SIGN_POS = 15;
35static constexpr int HALF_FLT_EXPONENT_POS = 10;
36static constexpr int HALF_FLT_EXPONENT_BITS = 5;
37static constexpr int HALF_FLT_EXPONENT_MASK = (1 << HALF_FLT_EXPONENT_BITS) - 1;
38static constexpr int HALF_FLT_EXPONENT_BIAS = 15;
39static constexpr int HALF_FLT_MANTISSA_BITS = 10;
40static constexpr int HALF_FLT_MANTISSA_MASK = (1 << HALF_FLT_MANTISSA_BITS) - 1;
41static constexpr int HALF_FLT_EXPONENT_AND_MANTISSA_MASK = HALF_FLT_MANTISSA_MASK + (HALF_FLT_EXPONENT_MASK << HALF_FLT_EXPONENT_POS);
42
45{
49};
50
52template <int RoundingMode>
54{
55 // Reinterpret the float as an uint32
56 uint32 value = BitCast<uint32>(inV);
57
58 // Extract exponent
59 uint32 exponent = (value >> FLOAT_EXPONENT_POS) & FLOAT_EXPONENT_MASK;
60
61 // Extract mantissa
62 uint32 mantissa = value & FLOAT_MANTISSA_MASK;
63
64 // Extract the sign and move it into the right spot for the half float (so we can just or it in at the end)
65 HalfFloat hf_sign = HalfFloat(value >> (FLOAT_SIGN_POS - HALF_FLT_SIGN_POS)) & (1 << HALF_FLT_SIGN_POS);
66
67 // Check NaN or INF
68 if (exponent == FLOAT_EXPONENT_MASK) // NaN or INF
69 return hf_sign | (mantissa == 0? HALF_FLT_INF : HALF_FLT_NANQ);
70
71 // Rebias the exponent for half floats
72 int rebiased_exponent = int(exponent) - FLOAT_EXPONENT_BIAS + HALF_FLT_EXPONENT_BIAS;
73
74 // Check overflow to infinity
75 if (rebiased_exponent >= HALF_FLT_EXPONENT_MASK)
76 {
77 bool round_up = RoundingMode == ROUND_TO_NEAREST || (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF);
78 return hf_sign | (round_up? HALF_FLT_INF : HALF_FLT_MAX);
79 }
80
81 // Check underflow to zero
82 if (rebiased_exponent < -HALF_FLT_MANTISSA_BITS)
83 {
84 bool round_up = RoundingMode != ROUND_TO_NEAREST && (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF) && (value & FLOAT_EXPONENT_AND_MANTISSA_MASK) != 0;
85 return hf_sign | (round_up? 1 : 0);
86 }
87
88 HalfFloat hf_exponent;
89 int shift;
90 if (rebiased_exponent <= 0)
91 {
92 // Underflow to denormalized number
93 hf_exponent = 0;
94 mantissa |= 1 << FLOAT_MANTISSA_BITS; // Add the implicit 1 bit to the mantissa
95 shift = FLOAT_MANTISSA_BITS - HALF_FLT_MANTISSA_BITS + 1 - rebiased_exponent;
96 }
97 else
98 {
99 // Normal half float
100 hf_exponent = HalfFloat(rebiased_exponent << HALF_FLT_EXPONENT_POS);
101 shift = FLOAT_MANTISSA_BITS - HALF_FLT_MANTISSA_BITS;
102 }
103
104 // Compose the half float
105 HalfFloat hf_mantissa = HalfFloat(mantissa >> shift);
106 HalfFloat hf = hf_sign | hf_exponent | hf_mantissa;
107
108 // Calculate the remaining bits that we're discarding
109 uint remainder = mantissa & ((1 << shift) - 1);
110
111 if constexpr (RoundingMode == ROUND_TO_NEAREST)
112 {
113 // Round to nearest
114 uint round_threshold = 1 << (shift - 1);
115 if (remainder > round_threshold // Above threshold, we must always round
116 || (remainder == round_threshold && (hf_mantissa & 1))) // When equal, round to nearest even
117 hf++; // May overflow to infinity
118 }
119 else
120 {
121 // Round up or down (truncate) depending on the rounding mode
122 bool round_up = (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF) && remainder != 0;
123 if (round_up)
124 hf++; // May overflow to infinity
125 }
126
127 return hf;
128}
129
131template <int RoundingMode>
132JPH_INLINE HalfFloat FromFloat(float inV)
133{
134#ifdef JPH_USE_F16C
135 union
136 {
137 __m128i u128;
138 HalfFloat u16[8];
139 } hf;
140 __m128 val = _mm_load_ss(&inV);
141 switch (RoundingMode)
142 {
143 case ROUND_TO_NEG_INF:
144 hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_NEG_INF);
145 break;
146 case ROUND_TO_POS_INF:
147 hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_POS_INF);
148 break;
149 case ROUND_TO_NEAREST:
150 hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_NEAREST_INT);
151 break;
152 }
153 return hf.u16[0];
154#else
155 return FromFloatFallback<RoundingMode>(inV);
156#endif
157}
158
161{
162 // Unpack half floats to 4 uint32's
163 UVec4 value = inValue.Expand4Uint16Lo();
164
165 // Normal half float path, extract the exponent and mantissa, shift them into place and update the exponent bias
166 UVec4 exponent_mantissa = UVec4::sAnd(value, UVec4::sReplicate(HALF_FLT_EXPONENT_AND_MANTISSA_MASK)).LogicalShiftLeft<FLOAT_EXPONENT_POS - HALF_FLT_EXPONENT_POS>() + UVec4::sReplicate((FLOAT_EXPONENT_BIAS - HALF_FLT_EXPONENT_BIAS) << FLOAT_EXPONENT_POS);
167
168 // Denormalized half float path, renormalize the float
169 UVec4 exponent_mantissa_denormalized = ((exponent_mantissa + UVec4::sReplicate(1 << FLOAT_EXPONENT_POS)).ReinterpretAsFloat() - UVec4::sReplicate((FLOAT_EXPONENT_BIAS - HALF_FLT_EXPONENT_BIAS + 1) << FLOAT_EXPONENT_POS).ReinterpretAsFloat()).ReinterpretAsInt();
170
171 // NaN / INF path, set all exponent bits
172 UVec4 exponent_mantissa_nan_inf = UVec4::sOr(exponent_mantissa, UVec4::sReplicate(FLOAT_EXPONENT_MASK << FLOAT_EXPONENT_POS));
173
174 // Get the exponent to determine which of the paths we should take
175 UVec4 exponent_mask = UVec4::sReplicate(HALF_FLT_EXPONENT_MASK << HALF_FLT_EXPONENT_POS);
176 UVec4 exponent = UVec4::sAnd(value, exponent_mask);
177 UVec4 is_denormalized = UVec4::sEquals(exponent, UVec4::sZero());
178 UVec4 is_nan_inf = UVec4::sEquals(exponent, exponent_mask);
179
180 // Select the correct result
181 UVec4 result_exponent_mantissa = UVec4::sSelect(UVec4::sSelect(exponent_mantissa, exponent_mantissa_nan_inf, is_nan_inf), exponent_mantissa_denormalized, is_denormalized);
182
183 // Extract the sign bit and shift it to the left
184 UVec4 sign = UVec4::sAnd(value, UVec4::sReplicate(1 << HALF_FLT_SIGN_POS)).LogicalShiftLeft<FLOAT_SIGN_POS - HALF_FLT_SIGN_POS>();
185
186 // Construct the float
187 return UVec4::sOr(sign, result_exponent_mantissa).ReinterpretAsFloat();
188}
189
191JPH_INLINE Vec4 ToFloat(UVec4Arg inValue)
192{
193#if defined(JPH_USE_F16C)
194 return _mm_cvtph_ps(inValue.mValue);
195#elif defined(JPH_USE_NEON)
196 return vcvt_f32_f16(vreinterpret_f16_u32(vget_low_u32(inValue.mValue)));
197#else
198 return ToFloatFallback(inValue);
199#endif
200}
201
202} // HalfFloatConversion
203
unsigned int uint
Definition: Core.h:452
#define JPH_NAMESPACE_END
Definition: Core.h:378
std::uint32_t uint32
Definition: Core.h:455
#define JPH_NAMESPACE_BEGIN
Definition: Core.h:372
std::uint16_t uint16
Definition: Core.h:454
uint16 HalfFloat
Definition: HalfFloat.h:11
Definition: UVec4.h:12
JPH_INLINE UVec4 LogicalShiftLeft() const
Shift all components by Count bits to the left (filling with zeros from the left)
JPH_INLINE UVec4 Expand4Uint16Lo() const
Takes the lower 4 16 bits and expands them to X, Y, Z and W.
Definition: UVec4.inl:460
static JPH_INLINE UVec4 sReplicate(uint32 inV)
Replicate int inV across all components.
Definition: UVec4.inl:56
static JPH_INLINE UVec4 sAnd(UVec4Arg inV1, UVec4Arg inV2)
Logical and (component wise)
Definition: UVec4.inl:199
static JPH_INLINE UVec4 sEquals(UVec4Arg inV1, UVec4Arg inV2)
Equals (component wise)
Definition: UVec4.inl:143
static JPH_INLINE UVec4 sOr(UVec4Arg inV1, UVec4Arg inV2)
Logical or (component wise)
Definition: UVec4.inl:171
Type mValue
Definition: UVec4.h:211
static JPH_INLINE UVec4 sZero()
Vector with all zeros.
Definition: UVec4.inl:45
static JPH_INLINE UVec4 sSelect(UVec4Arg inV1, UVec4Arg inV2, UVec4Arg inControl)
Component wise select, returns inV1 when highest bit of inControl = 0 and inV2 when highest bit of in...
Definition: UVec4.inl:157
JPH_INLINE Vec4 ReinterpretAsFloat() const
Reinterpret UVec4 as a Vec4 (doesn't change the bits)
Definition: UVec4.inl:337
Definition: Vec4.h:14
Definition: HalfFloat.h:21
Vec4 ToFloatFallback(UVec4Arg inValue)
Convert 4 half floats (lower 64 bits) to floats, fallback version when no intrinsics available.
Definition: HalfFloat.h:160
JPH_INLINE Vec4 ToFloat(UVec4Arg inValue)
Convert 4 half floats (lower 64 bits) to floats.
Definition: HalfFloat.h:191
JPH_INLINE HalfFloat FromFloat(float inV)
Convert a float (32-bits) to a half float (16-bits)
Definition: HalfFloat.h:132
ERoundingMode
Define half-float rounding modes.
Definition: HalfFloat.h:45
@ ROUND_TO_NEG_INF
Round to negative infinity.
Definition: HalfFloat.h:46
@ ROUND_TO_POS_INF
Round to positive infinity.
Definition: HalfFloat.h:47
@ ROUND_TO_NEAREST
Round to nearest value.
Definition: HalfFloat.h:48
HalfFloat FromFloatFallback(float inV)
Convert a float (32-bits) to a half float (16-bits), fallback version when no intrinsics available.
Definition: HalfFloat.h:53