flatten 20260225

This commit is contained in:
Timothy Prepscius
2026-02-25 12:43:06 -05:00
commit 996151a56a
521 changed files with 46722 additions and 0 deletions

13
tjp/core/math/Float16.h Normal file
View File

@@ -0,0 +1,13 @@
// 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 {
struct Float16;
Float16& set(Float16 &lhs, float rhs);
} // namespace

74
tjp/core/math/Float16.hpp Normal file
View File

@@ -0,0 +1,74 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include "../types/Types.h"
#include "Float16.h"
namespace tjp::core {
struct Float16
{
using V = u16;
V v;
struct Bits { V v; };
constexpr explicit Float16(Bits bits) :
v(bits.v)
{
}
constexpr Float16() :
v(0)
{}
constexpr Float16(const Float16 &rhs) :
v(rhs.v)
{}
constexpr Float16(float rhs) :
v(convert(rhs))
{
}
constexpr Float16(double rhs) :
v(convert((float)rhs))
{
}
static constexpr V convert(float rhs);
static constexpr float convert(V rhs);
static constexpr Float16 max();
static constexpr Float16 min__();
static constexpr Float16 negative_max();
auto ordered() const
{
u16 sign = v >> 15;
if (sign == 0)
return v | 0x8000;
else
return ~v;
}
operator float() const;
operator double() const;
Float16 &operator =(float rhs)
{
return set(*this, rhs);
}
Float16 &operator =(double rhs)
{
return set(*this, rhs);
}
} ;
} // namespace
#include "Float16.inl"

186
tjp/core/math/Float16.inl Normal file
View File

@@ -0,0 +1,186 @@
// 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

113
tjp/core/math/_tests/Float16.cpp Executable file
View File

@@ -0,0 +1,113 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/math/Float16.hpp>
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/testing/catch.hpp>
#include <random>
namespace tjp::core {
namespace {
float randomFloat() {
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_int_distribution<uint32_t> dist;
uint32_t bits = dist(gen);
float value;
std::memcpy(&value, &bits, sizeof(value));
return value;
}
float randomFloatInRange(float l, float r) {
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_real_distribution<float> dist(l, r);
return dist(gen);
}
SCENARIO("core::math::Float16")
{
GIVEN("nothing")
{
WHEN("tested all floats")
{
bool all_passed = true;
auto testCount = 100000;
for (auto i=0; i<testCount; ++i)
{
auto a = randomFloat();
auto t = Float16(a);
auto b = (float)t;
auto r16_min = (float)Float16::negative_max();
auto r16_max = (float)Float16::max();
if (isnan(b))
{
auto was_nan = isnan(a);
all_passed = all_passed && was_nan;
}
else
if (isinf(b))
{
auto was_out_of_range = isinf(a) || a > r16_max || a < r16_min;
all_passed = all_passed && was_out_of_range;
}
else
{
auto relative_epsilon = 0.001f; // relative tolerance
auto absolute_epsilon = 0.001f; // absolute tolerance
auto d = std::abs(b - a);
auto epsilon = std::max(relative_epsilon * std::abs(a), absolute_epsilon);
all_passed = all_passed && (d < epsilon);
if (!all_passed)
REQUIRE(false);
}
if (!all_passed)
REQUIRE(false);
}
REQUIRE(all_passed);
}
WHEN("tested valid float16s")
{
bool all_passed = true;
auto testCount = 100000;
for (auto i=0; i<testCount; ++i)
{
auto r16_min = (float)Float16::negative_max();
auto r16_max = (float)Float16::max();
auto a = randomFloatInRange(r16_min, r16_max);
auto t = Float16(a);
auto b = (float)t;
auto relative_epsilon = 0.001f; // relative tolerance
auto absolute_epsilon = 0.001f; // absolute tolerance
auto d = std::abs(b - a);
auto epsilon = std::max(relative_epsilon * std::abs(a), absolute_epsilon);
all_passed = all_passed && (d < epsilon);
}
REQUIRE(all_passed);
}
}
}
} // namespace
} // namespace