Files
core_zero/tjp/core/math/Float16.inl
Timothy Prepscius 996151a56a flatten 20260225
2026-02-25 12:43:06 -05:00

187 lines
3.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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