187 lines
3.1 KiB
C++
187 lines
3.1 KiB
C++
// License: Modified MIT (NON-AI)
|
||
// See the LICENSE file in the root directory for license information.
|
||
// Copyright 2025 Timothy Prepscius
|
||
|
||
#pragma once
|
||
|
||
namespace tjp::core {
|
||
|
||
inline
|
||
Float16::operator float() const
|
||
{
|
||
return convert(v);
|
||
}
|
||
|
||
inline
|
||
Float16::operator double() const
|
||
{
|
||
return (float)*this;
|
||
}
|
||
|
||
inline
|
||
bool operator <(const Float16 &lhs, const Float16 &rhs)
|
||
{
|
||
return lhs.ordered() < rhs.ordered();
|
||
}
|
||
|
||
inline
|
||
bool operator >(const Float16 &lhs, const Float16 &rhs)
|
||
{
|
||
return lhs.ordered() > rhs.ordered();
|
||
}
|
||
|
||
inline
|
||
bool operator ==(const Float16 &lhs, const Float16 &rhs)
|
||
{
|
||
return lhs.v == rhs.v;
|
||
}
|
||
|
||
inline
|
||
bool operator !=(const Float16 &lhs, const Float16 &rhs)
|
||
{
|
||
return lhs.v != rhs.v;
|
||
}
|
||
|
||
// ---
|
||
|
||
inline
|
||
constexpr Float16 Float16::max()
|
||
{
|
||
return Float16(Float16::Bits { .v = 0x7BFF });
|
||
}
|
||
|
||
inline
|
||
constexpr Float16 Float16::negative_max()
|
||
{
|
||
return Float16(Float16::Bits { .v = 0xFBFF });
|
||
}
|
||
|
||
inline
|
||
constexpr Float16 Float16::min__()
|
||
{
|
||
return Float16(Float16::Bits { .v = 0x0400 });
|
||
}
|
||
|
||
// ---
|
||
|
||
union Float16_Float32Bits {
|
||
float f;
|
||
uint32_t u;
|
||
};
|
||
|
||
inline
|
||
constexpr float Float16::convert(V v)
|
||
{
|
||
uint16_t hv = v;
|
||
uint16_t sign = (hv >> 15) & 0x1;
|
||
uint16_t exp = (hv >> 10) & 0x1F;
|
||
uint16_t frac = hv & 0x3FF;
|
||
|
||
uint32_t sign32 = sign << 31;
|
||
uint32_t exp32 = 0, frac32 = 0;
|
||
|
||
if (exp == 0) {
|
||
if (frac == 0) {
|
||
// Zero
|
||
exp32 = 0;
|
||
frac32 = 0;
|
||
} else {
|
||
// Subnormal: normalize
|
||
int e = -14; // unbiased exponent for half subnormals
|
||
uint32_t f = frac;
|
||
while ((f & 0x400) == 0) {
|
||
f <<= 1;
|
||
e--;
|
||
}
|
||
f &= 0x3FF;
|
||
exp32 = (e + 127) << 23;
|
||
frac32 = f << 13;
|
||
}
|
||
}
|
||
else if (exp == 0x1F) {
|
||
// Inf or NaN
|
||
exp32 = 0xFF << 23;
|
||
if (frac == 0) {
|
||
// Inf
|
||
frac32 = 0;
|
||
} else {
|
||
// NaN – preserve payload, set quiet NaN bit
|
||
frac32 = (frac << 13) | 0x400000;
|
||
}
|
||
}
|
||
else {
|
||
// Normalized
|
||
int e = exp - 15 + 127;
|
||
exp32 = (uint32_t)e << 23;
|
||
frac32 = (uint32_t)frac << 13;
|
||
}
|
||
|
||
auto u = sign32 | exp32 | frac32;
|
||
Float16_Float32Bits result { .u = u };
|
||
return result.f;
|
||
}
|
||
|
||
inline
|
||
constexpr Float16::V Float16::convert(float rhs)
|
||
{
|
||
Float16_Float32Bits result { .f = rhs };
|
||
uint32_t bits = result.u;
|
||
|
||
uint32_t sign = (bits >> 31) & 0x1;
|
||
int32_t exp = (bits >> 23) & 0xFF;
|
||
uint32_t frac = bits & 0x7FFFFF;
|
||
|
||
uint16_t sign16 = sign << 15;
|
||
uint16_t exp16 = 0;
|
||
uint16_t frac16 = 0;
|
||
|
||
if (exp == 0) {
|
||
// Zero or subnormal in float
|
||
exp16 = 0;
|
||
frac16 = 0; // Flush subnormals to zero
|
||
}
|
||
else if (exp == 0xFF) {
|
||
// Inf or NaN
|
||
exp16 = 0x1F;
|
||
frac16 = frac ? 0x200 : 0; // Preserve a quiet NaN bit
|
||
}
|
||
else {
|
||
// Normalized float
|
||
int newExp = exp - 127 + 15;
|
||
if (newExp >= 0x1F) {
|
||
// Overflow → Inf
|
||
exp16 = 0x1F;
|
||
frac16 = 0;
|
||
}
|
||
else if (newExp <= 0) {
|
||
// Underflow → subnormal or zero
|
||
if (newExp < -10) {
|
||
exp16 = 0;
|
||
frac16 = 0;
|
||
}
|
||
else {
|
||
// Subnormal half
|
||
uint32_t mant = (frac | 0x800000) >> (1 - newExp);
|
||
frac16 = mant >> 13;
|
||
exp16 = 0;
|
||
}
|
||
}
|
||
else {
|
||
exp16 = newExp;
|
||
frac16 = frac >> 13;
|
||
}
|
||
}
|
||
|
||
Float16::V v = sign16 | (exp16 << 10) | frac16;
|
||
return v;
|
||
}
|
||
|
||
inline
|
||
Float16& set(Float16 &lhs, float rhs)
|
||
{
|
||
lhs.v = Float16::convert(rhs);
|
||
return lhs;
|
||
}
|
||
|
||
} // namespace
|