[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
|
#endif
|
||||||
|
|
||||||
uint16_t float_to_half(float value);
|
// Similar to the C++ implementation of XMConvertFloatToHalf and
|
||||||
float half_to_float(uint16_t value);
|
// 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/
|
// https://locklessinc.com/articles/sat_arithmetic/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
Loading…
Reference in New Issue