Files
ww/lib/strconv/stof_test.ww

215 lines
8.4 KiB
Plaintext

// Ports ref/hare/strconv/stof.ha's @test vectors (stof64 / stof32 /
// stofhex). Comparisons are BIT-level (math.f64bits / f32bits) so a
// sign flip (-0.0 vs 0.0) or a 1-ulp miss fails the row rather than
// silently passing on IEEE `==`. NaN via math.isnan; ±Inf via
// f64frombits(INF_BITS) (ww math has no NAN/INF f32/f64 const — see
// math/floats.ww). Hex extremes (Hare's math::F64_MAX_NORMAL etc.,
// absent in ww math) assert against the IEEE-754 bit patterns directly.
package strconv_test;
import strconv;
import math;
fn chk64(s: str, b: strconv.base, want: f64) bool = {
match (strconv.stof64(s, b)) {
case let v: f64 => { return math.f64bits(v) == math.f64bits(want); };
case let e: strconv.invalid => { return false; };
case let e: strconv.overflow => { return false; };
};
return false;
};
fn chk32(s: str, b: strconv.base, want: f32) bool = {
match (strconv.stof32(s, b)) {
case let v: f32 => { return math.f32bits(v) == math.f32bits(want); };
case let e: strconv.invalid => { return false; };
case let e: strconv.overflow => { return false; };
};
return false;
};
fn ovf64(s: str, b: strconv.base) bool = {
match (strconv.stof64(s, b)) {
case let v: f64 => { return false; };
case let e: strconv.invalid => { return false; };
case let e: strconv.overflow => { return true; };
};
return false;
};
fn ovf32(s: str, b: strconv.base) bool = {
match (strconv.stof32(s, b)) {
case let v: f32 => { return false; };
case let e: strconv.invalid => { return false; };
case let e: strconv.overflow => { return true; };
};
return false;
};
// inv64 — the invalid byte-index, or -1 if not invalid.
fn inv64(s: str) i32 = {
match (strconv.stof64(s, strconv.base.DEC)) {
case let v: f64 => { return -1; };
case let e: strconv.invalid => { return (e: i32); };
case let e: strconv.overflow => { return -1; };
};
return -1;
};
fn inv32(s: str) i32 = {
match (strconv.stof32(s, strconv.base.DEC)) {
case let v: f32 => { return -1; };
case let e: strconv.invalid => { return (e: i32); };
case let e: strconv.overflow => { return -1; };
};
return -1;
};
fn nan64(s: str) bool = {
match (strconv.stof64(s, strconv.base.DEC)) {
case let v: f64 => { return math.isnan(v); };
case let e: strconv.invalid => { return false; };
case let e: strconv.overflow => { return false; };
};
return false;
};
fn nan32(s: str) bool = {
match (strconv.stof32(s, strconv.base.DEC)) {
case let v: f32 => { return math.isnan((v: f64)); };
case let e: strconv.invalid => { return false; };
case let e: strconv.overflow => { return false; };
};
return false;
};
// ref/hare/strconv/stof.ha:530.
@test fn stof64_dec() void = {
let inf: f64 = math.f64frombits(math.INF_BITS);
let ninf: f64 = math.f64frombits(0xFFF0000000000000u64);
let nzero: f64 = math.f64frombits(1u64 << 63u64); // -0.0 (1<<63 dodges the I64_MIN-literal emit bug #144)
let subn: f64 = math.f64frombits(0x0000000000000001u64); // 5e-324
assert(!(!chk64("0", strconv.base.DEC, 0.0)));
assert(!(!chk64("200", strconv.base.DEC, 200.0)));
assert(!(!chk64("12345", strconv.base.DEC, 12345.0)));
assert(!(!chk64("+112233445566778899", strconv.base.DEC, 1.122334455667789e17)));
assert(!(!chk64("3.14", strconv.base.DEC, 3.14)));
assert(!(!chk64("2.99792458E+8", strconv.base.DEC, 299792458.0)));
assert(!(!chk64("6.022e23", strconv.base.DEC, 6.022e23)));
assert(!(!ovf64("1e310", strconv.base.DEC)));
assert(!(!chk64("9007199254740991", strconv.base.DEC, 9007199254740991.0)));
assert(!(!chk64("90071992547409915", strconv.base.DEC, 90071992547409920.0)));
assert(!(!chk64("90071992547409925", strconv.base.DEC, 90071992547409920.0)));
assert(!(!chk64("2.2250738585072014e-308", strconv.base.DEC, 2.2250738585072014e-308)));
assert(!(!chk64("-1e-324", strconv.base.DEC, nzero)));
assert(!(!chk64("5e-324", strconv.base.DEC, subn)));
// Decimal-engine edge probes migrated here when the white-box
// decimaltest.ww retired (#16): the external-test idiom can't reach
// strconv's unexported decimal fns, so these two black-box rows pin
// the only edges not already guaranteed transitively. C1: all-9s
// round-up carry + decimal_round dp>18 boundary. C2: nd>19 skips
// eisel-lemire, forcing the pure-decimal slow path (extra trim).
// Micro-gap (rule-7, honest): decimal_shift's k=0 early-return no-op
// is a trivial guard, not reached transitively by any slow-path input
// — consciously un-migrated, not silently dropped.
assert(!(!chk64("9999999999999999", strconv.base.DEC, 1.0e16)));
assert(!(!chk64("100000000000000000001", strconv.base.DEC, 1.0e20)));
assert(!(inv64("") != 0));
assert(!(inv64("0ZO") != 1));
assert(!(inv64("1.23ezz") != 5));
assert(!(!chk64("Infinity", strconv.base.DEC, inf)));
assert(!(!chk64("+Infinity", strconv.base.DEC, inf)));
assert(!(!chk64("-Infinity", strconv.base.DEC, ninf)));
assert(!(!chk64("infinity", strconv.base.DEC, inf)));
assert(!(!chk64("inFinIty", strconv.base.DEC, inf)));
assert(!(!chk64("-infinity", strconv.base.DEC, ninf)));
assert(!(!chk64("-infiNity", strconv.base.DEC, ninf)));
assert(!(!nan64("NaN")));
assert(!(!nan64("nan")));
assert(!(!nan64("naN")));
};
// ref/hare/strconv/stof.ha:560.
@test fn stof32_dec() void = {
let inf: f32 = math.f32frombits(0x7F800000u32);
let ninf: f32 = math.f32frombits(0xFF800000u32);
let nzero: f32 = math.f32frombits(0x80000000u32); // -0.0
let subn: f32 = math.f32frombits(0x00000001u32); // 1e-45
assert(!(!chk32("0", strconv.base.DEC, 0.0f32)));
assert(!(!chk32("1e10", strconv.base.DEC, 1.0e10f32)));
assert(!(!chk32("299792458", strconv.base.DEC, 299792458.0f32)));
assert(!(!chk32("6.022e23", strconv.base.DEC, 6.022e23f32)));
assert(!(!ovf32("1e40", strconv.base.DEC)));
assert(!(!chk32("16777215", strconv.base.DEC, 16777215.0f32)));
assert(!(!chk32("167772155", strconv.base.DEC, 167772160.0f32)));
assert(!(!chk32("167772145", strconv.base.DEC, 167772140.0f32)));
assert(!(!chk32("6.62607015e-34", strconv.base.DEC, 6.62607015e-34f32)));
assert(!(!chk32("1.1754944e-38", strconv.base.DEC, 1.1754944e-38f32)));
assert(!(!chk32("-1e-50", strconv.base.DEC, nzero)));
assert(!(!chk32("1e-45", strconv.base.DEC, subn)));
assert(!(inv32("") != 0));
assert(!(inv32("0ZO") != 1));
assert(!(inv32("1.23e-zz") != 6));
assert(!(!chk32("Infinity", strconv.base.DEC, inf)));
assert(!(!chk32("+Infinity", strconv.base.DEC, inf)));
assert(!(!chk32("-Infinity", strconv.base.DEC, ninf)));
assert(!(!chk32("infinity", strconv.base.DEC, inf)));
assert(!(!chk32("inFinIty", strconv.base.DEC, inf)));
assert(!(!chk32("-infinity", strconv.base.DEC, ninf)));
assert(!(!chk32("-infiniTy", strconv.base.DEC, ninf)));
assert(!(!nan32("NaN")));
assert(!(!nan32("nan")));
assert(!(!nan32("naN")));
assert(!(!chk32("9.19100241453305036800e+20", strconv.base.DEC, 9.19100241453305036800e+20f32)));
};
// ref/hare/strconv/stof.ha:590. Hex-float surface-form literals (0x1.fp-2,
// math::F64_MAX_NORMAL, …) are spelled as IEEE-754 bit patterns since ww
// has no hex-float literal lexer / no F*_MAX_NORMAL math consts.
@test fn stof64_hex() void = {
assert(!(!chk64("0p0", strconv.base.HEX, 0.0)));
assert(!(!chk64("1p0", strconv.base.HEX, 1.0)));
assert(!(!chk64("-1p0", strconv.base.HEX_LOWER, -1.0)));
// 0x1.fp-2 = 1.9375 * 2^-2 = 0.484375 (exact).
assert(!(!chk64("1.fp-2", strconv.base.HEX, 0.484375)));
// F64_MAX_NORMAL.
if (!chk64("1.fffffffffffffp+1023", strconv.base.HEX,
math.f64frombits(0x7FEFFFFFFFFFFFFFu64))) { abort(); };
// F64_MIN_NORMAL.
if (!chk64("1.0000000000000p-1022", strconv.base.HEX,
math.f64frombits(0x0010000000000000u64))) { abort(); };
// F64_MIN_SUBNORMAL.
if (!chk64("0.0000000000001p-1022", strconv.base.HEX,
math.f64frombits(0x0000000000000001u64))) { abort(); };
assert(!(!ovf64("1p+1024", strconv.base.HEX)));
assert(!(!chk64("0.00000000000001p-1022", strconv.base.HEX, 0.0)));
};
@test fn stof32_hex() void = {
assert(!(!chk32("0p0", strconv.base.HEX, 0.0f32)));
assert(!(!chk32("1p0", strconv.base.HEX, 1.0f32)));
assert(!(!chk32("-1p0", strconv.base.HEX, -1.0f32)));
assert(!(!chk32("1.fp-2", strconv.base.HEX, 0.484375f32)));
// F32_MAX_NORMAL.
if (!chk32("1.fffffd586b834p+127", strconv.base.HEX,
math.f32frombits(0x7F7FFFFFu32))) { abort(); };
// F32_MIN_NORMAL.
assert(!(!chk32("1.0p-126", strconv.base.HEX, math.f32frombits(0x00800000u32))));
// F32_MIN_SUBNORMAL.
assert(!(!chk32("1.6p-150", strconv.base.HEX, math.f32frombits(0x00000001u32))));
assert(!(!ovf32("1.0p+128", strconv.base.HEX)));
assert(!(!chk32("1.0p-151", strconv.base.HEX, 0.0f32)));
};