[Base] More flexible Xenos float16 conversion functions
This commit is contained in:
parent
e3dd873892
commit
12ff951972
|
@ -1,69 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/base/math.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
// TODO(benvanik): replace with alternate implementation.
|
||||
// XMConvertFloatToHalf
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
uint16_t float_to_half(float value) {
|
||||
uint32_t Result;
|
||||
uint32_t IValue = (reinterpret_cast<uint32_t*>(&value))[0];
|
||||
uint32_t Sign = (IValue & 0x80000000U) >> 16U;
|
||||
IValue = IValue & 0x7FFFFFFFU; // Hack off the sign
|
||||
if (IValue > 0x47FFEFFFU) {
|
||||
// The number is too large to be represented as a half. Saturate to
|
||||
// infinity.
|
||||
Result = 0x7FFFU;
|
||||
} else {
|
||||
if (IValue < 0x38800000U) {
|
||||
// The number is too small to be represented as a normalized half.
|
||||
// Convert it to a denormalized value.
|
||||
uint32_t Shift = 113U - (IValue >> 23U);
|
||||
IValue = (0x800000U | (IValue & 0x7FFFFFU)) >> Shift;
|
||||
} else {
|
||||
// Rebias the exponent to represent the value as a normalized half.
|
||||
IValue += 0xC8000000U;
|
||||
}
|
||||
Result = ((IValue + 0x0FFFU + ((IValue >> 13U) & 1U)) >> 13U) & 0x7FFFU;
|
||||
}
|
||||
return (uint16_t)(Result | Sign);
|
||||
}
|
||||
|
||||
// TODO(benvanik): replace with alternate implementation.
|
||||
// XMConvertHalfToFloat
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
float half_to_float(uint16_t value) {
|
||||
uint32_t Mantissa = (uint32_t)(value & 0x03FF);
|
||||
uint32_t Exponent;
|
||||
if ((value & 0x7C00) != 0) {
|
||||
// The value is normalized
|
||||
Exponent = (uint32_t)((value >> 10) & 0x1F);
|
||||
} else if (Mantissa != 0) {
|
||||
// The value is denormalized
|
||||
// Normalize the value in the resulting float
|
||||
Exponent = 1;
|
||||
do {
|
||||
Exponent--;
|
||||
Mantissa <<= 1;
|
||||
} while ((Mantissa & 0x0400) == 0);
|
||||
Mantissa &= 0x03FF;
|
||||
} else {
|
||||
// The value is zero
|
||||
Exponent = (uint32_t)-112;
|
||||
}
|
||||
uint32_t Result = ((value & 0x8000) << 16) | // Sign
|
||||
((Exponent + 112) << 23) | // Exponent
|
||||
(Mantissa << 13); // Mantissa
|
||||
return *reinterpret_cast<float*>(&Result);
|
||||
}
|
||||
|
||||
} // namespace xe
|
|
@ -378,8 +378,65 @@ int64_t m128_i64(const __m128& v) {
|
|||
}
|
||||
#endif
|
||||
|
||||
uint16_t float_to_half(float value);
|
||||
float half_to_float(uint16_t value);
|
||||
// Similar to the C++ implementation of XMConvertFloatToHalf and
|
||||
// XMConvertHalfToFloat from DirectXMath 3.00 (pre-3.04, which switched from the
|
||||
// Xenos encoding to IEEE 754), with the extended range instead of infinity and
|
||||
// NaN, and optionally with denormalized numbers - as used in vpkd3d128 (no
|
||||
// denormals, rounding towards zero) and on the Xenos (GL_OES_texture_float
|
||||
// alternative encoding).
|
||||
|
||||
inline uint16_t float_to_xenos_half(float value, bool preserve_denormal = false,
|
||||
bool round_to_nearest_even = false) {
|
||||
uint32_t integer_value = *reinterpret_cast<const uint32_t*>(&value);
|
||||
uint32_t abs_value = integer_value & 0x7FFFFFFFu;
|
||||
uint32_t result;
|
||||
if (abs_value >= 0x47FFE000u) {
|
||||
// Saturate.
|
||||
result = 0x7FFFu;
|
||||
} else {
|
||||
if (abs_value < 0x38800000u) {
|
||||
// The number is too small to be represented as a normalized half.
|
||||
if (preserve_denormal) {
|
||||
uint32_t shift =
|
||||
std::min(uint32_t(113u - (abs_value >> 23u)), uint32_t(24u));
|
||||
result = (0x800000u | (abs_value & 0x7FFFFFu)) >> shift;
|
||||
} else {
|
||||
result = 0u;
|
||||
}
|
||||
} else {
|
||||
// Rebias the exponent to represent the value as a normalized half.
|
||||
result = abs_value + 0xC8000000u;
|
||||
}
|
||||
if (round_to_nearest_even) {
|
||||
result += 0xFFFu + ((result >> 13u) & 1u);
|
||||
}
|
||||
result = (result >> 13u) & 0x7FFFu;
|
||||
}
|
||||
return uint16_t(result | ((integer_value & 0x80000000u) >> 16u));
|
||||
}
|
||||
|
||||
inline float xenos_half_to_float(uint16_t value,
|
||||
bool preserve_denormal = false) {
|
||||
uint32_t mantissa = value & 0x3FFu;
|
||||
uint32_t exponent = (value >> 10u) & 0x1Fu;
|
||||
if (!exponent) {
|
||||
if (!preserve_denormal) {
|
||||
mantissa = 0;
|
||||
} else if (mantissa) {
|
||||
// Normalize the value in the resulting float.
|
||||
// do { Exponent--; Mantissa <<= 1; } while ((Mantissa & 0x0400) == 0)
|
||||
uint32_t mantissa_lzcnt = xe::lzcnt(mantissa) - (32u - 11u);
|
||||
exponent = uint32_t(1 - int32_t(mantissa_lzcnt));
|
||||
mantissa = (mantissa << mantissa_lzcnt) & 0x3FFu;
|
||||
}
|
||||
if (!mantissa) {
|
||||
exponent = uint32_t(-112);
|
||||
}
|
||||
}
|
||||
uint32_t result = (uint32_t(value & 0x8000u) << 16u) |
|
||||
((exponent + 112u) << 23u) | (mantissa << 13u);
|
||||
return *reinterpret_cast<const float*>(&result);
|
||||
}
|
||||
|
||||
// https://locklessinc.com/articles/sat_arithmetic/
|
||||
template <typename T>
|
||||
|
|
Loading…
Reference in New Issue