diff --git a/lib/math/checked/checked_test.ww b/lib/math/checked/checked_test.ww index 111ad218..af7836fe 100644 --- a/lib/math/checked/checked_test.ww +++ b/lib/math/checked/checked_test.ww @@ -302,6 +302,37 @@ import types; if (checked.sat_subi64(9223372036854775800, -10) != types.I64_MAX) { let _: i32 = 1/0; }; }; +// The third/fourth rows of each sat_subu* test go beyond Hare's two +// vectors: a==b (exact-zero, non-saturating edge) and 0−U*_MAX (extreme +// operand forcing the deepest underflow). #28 reviewer hardening. +@test fn test_sat_subu8() void = { + if (checked.sat_subu8(250u8, 50u8) != 200u8) { let _: i32 = 1/0; }; + if (checked.sat_subu8(44u8, 100u8) != types.U8_MIN) { let _: i32 = 1/0; }; + if (checked.sat_subu8(100u8, 100u8) != 0u8) { let _: i32 = 1/0; }; + if (checked.sat_subu8(0u8, 255u8) != types.U8_MIN) { let _: i32 = 1/0; }; +}; + +@test fn test_sat_subu16() void = { + if (checked.sat_subu16(65530u16, 30u16) != 65500u16) { let _: i32 = 1/0; }; + if (checked.sat_subu16(14u16, 50u16) != types.U16_MIN) { let _: i32 = 1/0; }; + if (checked.sat_subu16(1000u16, 1000u16) != 0u16) { let _: i32 = 1/0; }; + if (checked.sat_subu16(0u16, 65535u16) != types.U16_MIN) { let _: i32 = 1/0; }; +}; + +@test fn test_sat_subu32() void = { + if (checked.sat_subu32(4294967290u32, 90u32) != 4294967200u32) { let _: i32 = 1/0; }; + if (checked.sat_subu32(4u32, 100u32) != types.U32_MIN) { let _: i32 = 1/0; }; + if (checked.sat_subu32(1000000u32, 1000000u32) != 0u32) { let _: i32 = 1/0; }; + if (checked.sat_subu32(0u32, 4294967295u32) != types.U32_MIN) { let _: i32 = 1/0; }; +}; + +@test fn test_sat_subu64() void = { + if (checked.sat_subu64(18446744073709551610u64, 10u64) != 18446744073709551600u64) { let _: i32 = 1/0; }; + if (checked.sat_subu64(44u64, 50u64) != types.U64_MIN) { let _: i32 = 1/0; }; + if (checked.sat_subu64(1000000u64, 1000000u64) != 0u64) { let _: i32 = 1/0; }; + if (checked.sat_subu64(0u64, 18446744073709551615u64) != types.U64_MIN) { let _: i32 = 1/0; }; +}; + @test fn test_sat_muli8() void = { if (checked.sat_muli8(11, 11) != 121) { let _: i32 = 1/0; }; if (checked.sat_muli8(12, 12) != types.I8_MAX) { let _: i32 = 1/0; }; @@ -341,6 +372,18 @@ import types; if (checked.sat_mulu32(65536u32, 65536u32) != types.U32_MAX) { let _: i32 = 1/0; }; }; +// Direct read of types::U*_MIN (all 0, ref/hare/types/limits.ha:30,36, +// 42,48) and RUNE_MIN ('\0', :54). The sat_subu* clamp cases above also +// exercise the U*_MIN, but assert the bare values here so a regression in +// lib/types surfaces standalone. +@test fn test_types_min() void = { + if (types.U8_MIN != 0u8) { let _: i32 = 1/0; }; + if (types.U16_MIN != 0u16) { let _: i32 = 1/0; }; + if (types.U32_MIN != 0u32) { let _: i32 = 1/0; }; + if (types.U64_MIN != 0u64) { let _: i32 = 1/0; }; + if (types.RUNE_MIN != '\0') { let _: i32 = 1/0; }; +}; + export fn main() i32 = { test_addi8(); test_addi16(); @@ -376,11 +419,16 @@ export fn main() i32 = { test_sat_subi16(); test_sat_subi32(); test_sat_subi64(); + test_sat_subu8(); + test_sat_subu16(); + test_sat_subu32(); + test_sat_subu64(); test_sat_muli8(); test_sat_muli16(); test_sat_muli32(); test_sat_mulu8(); test_sat_mulu16(); test_sat_mulu32(); + test_types_min(); return 0; }; diff --git a/lib/math/checked/saturating.ww b/lib/math/checked/saturating.ww index 27deb6c4..f42ef50c 100644 --- a/lib/math/checked/saturating.ww +++ b/lib/math/checked/saturating.ww @@ -14,9 +14,8 @@ // - the mul overflow compares need an explicit widening cast (no // implicit promotion in ww). // -// Deferred (faithful subset, Hare splits per type): sat_subu8/16/32/64 -// (reference types::U*_MIN, not yet exported by lib/types); sat_*z (no -// `size` type, #85); sat_muli64/sat_mulu64/sat_muli/sat_mulu (need +// Deferred (faithful subset, Hare splits per type): sat_*z (no `size` +// type, #85); sat_muli64/sat_mulu64/sat_muli/sat_mulu (need // math::mulu64); int/uint native-width sat_* (ww int is 64-bit — a width // divergence in the overflow boundary). @@ -120,6 +119,30 @@ export fn sat_subi64(a: i64, b: i64) i64 = { return res; }; +export fn sat_subu8(a: u8, b: u8) u8 = { + let res: u8 = a - b; + if (res > a) { return types.U8_MIN; }; + return res; +}; + +export fn sat_subu16(a: u16, b: u16) u16 = { + let res: u16 = a - b; + if (res > a) { return types.U16_MIN; }; + return res; +}; + +export fn sat_subu32(a: u32, b: u32) u32 = { + let res: u32 = a - b; + if (res > a) { return types.U32_MIN; }; + return res; +}; + +export fn sat_subu64(a: u64, b: u64) u64 = { + let res: u64 = a - b; + if (res > a) { return types.U64_MIN; }; + return res; +}; + export fn sat_muli8(a: i8, b: i8) i8 = { let fullres: int = a: int * b: int; let res: i8 = fullres: i8;