Julia interoperability

FloatByte <: AbstractFloat, with a deliberately curated surface: what is defined is tested against the draft; what is not defined errors loudly instead of silently borrowing host-float semantics.

Arithmetic and promotion

Base operators (+ - * / sqrt inv fma hypot abs min max clamp copysign and the transcendental names) work on same-format operands under the scoped default projection. Cross-format arithmetic through Base operators is an error pointing at the explicit seam; mixing with host floats promotes to Float64/Float32 (exact for every datum):

julia> using FloatBytes

julia> Binary8p4se(1.5) + 1.0          # promotes to Float64, exactly
2.5

julia> Binary8p4se(1.5) + Binary8p3se(1.0)
ERROR: ArgumentError: cross-format + between Binary8p4se and Binary8p3se is not defined; use apply(Add, Fr, ρ, x, y) or Convert
[...]

Host-float bridges

Decode is exact in both directions of the bridge; every host NaN collapses to the single NaN, both host zeros to the single zero, and construction from a host float is a single projection (no double rounding):

julia> Binary8p4se(-0.0) === zero(Binary8p4se)
true

julia> Float64(maxfinite(Binary8p4se))
224.0

julia> quantize(Binary8p4se, big"1.00000000000000000001")
Binary8p4se(0x40 ↦ 1.0)

Ordering, sorting, hashing

isless follows the P3109 total order: NaN sorts first — one order for predicates, sorting, and selection:

julia> v = [Binary8p4se(2.0), Binary8p4se(NaN), Binary8p4se(-1.0)];

julia> sort(v)
3-element Vector{Binary8p4se}:
 Binary8p4se(0x80 ↦ NaN)
 Binary8p4se(0xc0 ↦ -1.0)
 Binary8p4se(0x48 ↦ 2.0)

==, <, <= follow the draft comparison predicates (NaN compares false with everything, including itself); hash is on (format, code point) so Dict and Set behave.

Random values

rand(rng, F) draws uniformly over the finite datums of the format (a documented choice — never NaN or ±Inf).

Zero-copy bytes

Arrays are plain byte buffers: reinterpret(UInt8, ::Vector{F}) is zero-copy, sizeof(F) == 1, isbitstype(F).