lib/math: math::floats fold-2a issubnormalf64 + normalizef64

Ports the subnormal-normalize step of the f64 decompose half from
ref/hare/math/floats.ha: issubnormalf64 (floats.ha:179) and normalizef64
(floats.ha:256, the f64-multiply-on-subnormal that yields (f64, i64)).

frexpf64 (floats.ha:278) is held back, not ported: its Hare-exact zero
guard `n == 0f64` miscompiles. A no-decimal `0f64` literal used as an f64
comparison operand is materialized into a GPR and never moved to XMM, so
the UCOMISD reads a stale operand and `n == 0f64` is wrong for every n.
Both stages emit this identically, so the byte-id gates are blind to it.
`0.0` compiles correctly but substituting it would be a workaround
(rule 7), so frexpf64 waits for the cgen fix. normalizef64/issubnormalf64
touch neither the broken literal form nor any tuple-field comparison, so
they are correct and land now.
This commit is contained in:
2026-05-25 15:06:19 +09:00
parent 8182342a9d
commit 263257f2c1

View File

@@ -1,7 +1,15 @@
// floats — f64 classification, sign, and bit-reinterpret core. Ported
// from ref/hare/math/floats.ha (fold-1: the minimal classify/sign/bits
// surface). f32 variants, NAN/INF + magnitude consts, and
// frexp/ldexp/normalize/modfrac/nextafter are deferred to a later fold.
// floats — f64 classification, sign, bit-reinterpret core, and the
// subnormal-normalize step of the f64 decompose half. Ported from
// ref/hare/math/floats.ha (fold-1: classify/sign/bits; fold-2a:
// issubnormalf64/normalizef64). frexpf64 (floats.ha:278) is held back: its
// Hare-exact zero guard `n == 0f64` miscompiles — a no-decimal `0f64`
// literal in an f64 comparison is materialized into a GPR and never moved
// to XMM, so the compare reads a stale operand (both stages identically,
// so the byte-id gates are blind to it). `0.0` would dodge it, but that is
// a workaround (CLAUDE.md rule 7); frexpf64 lands once the cgen bug is
// fixed. f32 variants, NAN/INF + magnitude consts, and the
// ldexp/modfrac/nextafter family (need f64 DIVIDE + the INF const) are
// deferred to a later fold.
package math;
@@ -64,6 +72,15 @@ export fn isinf(n: f64) bool = {
return exp == F64_EXPONENT_MASK && mant == 0;
};
// Returns true if the given f64 is subnormal.
// ref/hare/math/floats.ha:179
export fn issubnormalf64(n: f64) bool = {
const bits = f64bits(n);
const mant = bits & F64_MANTISSA_MASK;
const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK;
return exp == 0 && mant != 0;
};
// Returns the absolute value of f64 n.
// ref/hare/math/floats.ha:195
export fn absf64(n: f64) f64 = {
@@ -102,3 +119,15 @@ export fn copysignf64(x: f64, y: f64) f64 = {
return f64frombits((f64bits(x) & ~F64_SIGN_MASK) |
(f64bits(y) & F64_SIGN_MASK));
};
// Takes a potentially subnormal f64 n and returns a normal f64 normal_float
// and an exponent exp such that n == normal_float * 2^{exp}.
// ref/hare/math/floats.ha:256
export fn normalizef64(n: f64) (f64, i64) = {
if (issubnormalf64(n)) {
const factor = 1i64 << (F64_MANTISSA_BITS: i64);
const normal_float = (n * (factor: f64));
return (normal_float, -(F64_MANTISSA_BITS: i64));
};
return (n, 0);
};