Path: blob/master/thirdparty/jolt_physics/Jolt/Math/HalfFloat.h
9913 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56#include <Jolt/Math/Vec4.h>7#include <Jolt/Core/FPException.h>89JPH_NAMESPACE_BEGIN1011using HalfFloat = uint16;1213// Define half float constant values14static 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;2021namespace HalfFloatConversion {2223// Layout of a float24static 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);3233// Layout of half float34static 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);4243/// Define half-float rounding modes44enum ERoundingMode45{46ROUND_TO_NEG_INF, ///< Round to negative infinity47ROUND_TO_POS_INF, ///< Round to positive infinity48ROUND_TO_NEAREST, ///< Round to nearest value49};5051/// Convert a float (32-bits) to a half float (16-bits), fallback version when no intrinsics available52template <int RoundingMode>53inline HalfFloat FromFloatFallback(float inV)54{55// Reinterpret the float as an uint3256uint32 value = BitCast<uint32>(inV);5758// Extract exponent59uint32 exponent = (value >> FLOAT_EXPONENT_POS) & FLOAT_EXPONENT_MASK;6061// Extract mantissa62uint32 mantissa = value & FLOAT_MANTISSA_MASK;6364// Extract the sign and move it into the right spot for the half float (so we can just or it in at the end)65HalfFloat hf_sign = HalfFloat(value >> (FLOAT_SIGN_POS - HALF_FLT_SIGN_POS)) & (1 << HALF_FLT_SIGN_POS);6667// Check NaN or INF68if (exponent == FLOAT_EXPONENT_MASK) // NaN or INF69return hf_sign | (mantissa == 0? HALF_FLT_INF : HALF_FLT_NANQ);7071// Rebias the exponent for half floats72int rebiased_exponent = int(exponent) - FLOAT_EXPONENT_BIAS + HALF_FLT_EXPONENT_BIAS;7374// Check overflow to infinity75if (rebiased_exponent >= HALF_FLT_EXPONENT_MASK)76{77bool round_up = RoundingMode == ROUND_TO_NEAREST || (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF);78return hf_sign | (round_up? HALF_FLT_INF : HALF_FLT_MAX);79}8081// Check underflow to zero82if (rebiased_exponent < -HALF_FLT_MANTISSA_BITS)83{84bool round_up = RoundingMode != ROUND_TO_NEAREST && (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF) && (value & FLOAT_EXPONENT_AND_MANTISSA_MASK) != 0;85return hf_sign | (round_up? 1 : 0);86}8788HalfFloat hf_exponent;89int shift;90if (rebiased_exponent <= 0)91{92// Underflow to denormalized number93hf_exponent = 0;94mantissa |= 1 << FLOAT_MANTISSA_BITS; // Add the implicit 1 bit to the mantissa95shift = FLOAT_MANTISSA_BITS - HALF_FLT_MANTISSA_BITS + 1 - rebiased_exponent;96}97else98{99// Normal half float100hf_exponent = HalfFloat(rebiased_exponent << HALF_FLT_EXPONENT_POS);101shift = FLOAT_MANTISSA_BITS - HALF_FLT_MANTISSA_BITS;102}103104// Compose the half float105HalfFloat hf_mantissa = HalfFloat(mantissa >> shift);106HalfFloat hf = hf_sign | hf_exponent | hf_mantissa;107108// Calculate the remaining bits that we're discarding109uint remainder = mantissa & ((1 << shift) - 1);110111if constexpr (RoundingMode == ROUND_TO_NEAREST)112{113// Round to nearest114uint round_threshold = 1 << (shift - 1);115if (remainder > round_threshold // Above threshold, we must always round116|| (remainder == round_threshold && (hf_mantissa & 1))) // When equal, round to nearest even117hf++; // May overflow to infinity118}119else120{121// Round up or down (truncate) depending on the rounding mode122bool round_up = (hf_sign == 0) == (RoundingMode == ROUND_TO_POS_INF) && remainder != 0;123if (round_up)124hf++; // May overflow to infinity125}126127return hf;128}129130/// Convert a float (32-bits) to a half float (16-bits)131template <int RoundingMode>132JPH_INLINE HalfFloat FromFloat(float inV)133{134#ifdef JPH_USE_F16C135FPExceptionDisableOverflow disable_overflow;136JPH_UNUSED(disable_overflow);137138union139{140__m128i u128;141HalfFloat u16[8];142} hf;143__m128 val = _mm_load_ss(&inV);144switch (RoundingMode)145{146case ROUND_TO_NEG_INF:147hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_NEG_INF);148break;149case ROUND_TO_POS_INF:150hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_POS_INF);151break;152case ROUND_TO_NEAREST:153hf.u128 = _mm_cvtps_ph(val, _MM_FROUND_TO_NEAREST_INT);154break;155}156return hf.u16[0];157#else158return FromFloatFallback<RoundingMode>(inV);159#endif160}161162/// Convert 4 half floats (lower 64 bits) to floats, fallback version when no intrinsics available163inline Vec4 ToFloatFallback(UVec4Arg inValue)164{165// Unpack half floats to 4 uint32's166UVec4 value = inValue.Expand4Uint16Lo();167168// Normal half float path, extract the exponent and mantissa, shift them into place and update the exponent bias169UVec4 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);170171// Denormalized half float path, renormalize the float172UVec4 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();173174// NaN / INF path, set all exponent bits175UVec4 exponent_mantissa_nan_inf = UVec4::sOr(exponent_mantissa, UVec4::sReplicate(FLOAT_EXPONENT_MASK << FLOAT_EXPONENT_POS));176177// Get the exponent to determine which of the paths we should take178UVec4 exponent_mask = UVec4::sReplicate(HALF_FLT_EXPONENT_MASK << HALF_FLT_EXPONENT_POS);179UVec4 exponent = UVec4::sAnd(value, exponent_mask);180UVec4 is_denormalized = UVec4::sEquals(exponent, UVec4::sZero());181UVec4 is_nan_inf = UVec4::sEquals(exponent, exponent_mask);182183// Select the correct result184UVec4 result_exponent_mantissa = UVec4::sSelect(UVec4::sSelect(exponent_mantissa, exponent_mantissa_nan_inf, is_nan_inf), exponent_mantissa_denormalized, is_denormalized);185186// Extract the sign bit and shift it to the left187UVec4 sign = UVec4::sAnd(value, UVec4::sReplicate(1 << HALF_FLT_SIGN_POS)).LogicalShiftLeft<FLOAT_SIGN_POS - HALF_FLT_SIGN_POS>();188189// Construct the float190return UVec4::sOr(sign, result_exponent_mantissa).ReinterpretAsFloat();191}192193/// Convert 4 half floats (lower 64 bits) to floats194JPH_INLINE Vec4 ToFloat(UVec4Arg inValue)195{196#if defined(JPH_USE_F16C)197return _mm_cvtph_ps(inValue.mValue);198#elif defined(JPH_USE_NEON)199return vcvt_f32_f16(vreinterpret_f16_u32(vget_low_u32(inValue.mValue)));200#else201return ToFloatFallback(inValue);202#endif203}204205} // HalfFloatConversion206207JPH_NAMESPACE_END208209210