from stdint import * import stdio import stdlib import testcheck import viperlib import string # SprintfTest: runtime verification of viperlib.snprintf %a/%A against glibc printf export def main() -> int: testcheck.begin("SprintfTest: %a/%A runtime verification") buf: bytes = stdlib.malloc(256) # ---------- default precision (core correctness) ---------- testcheck.section("default precision") v1: double = 1.0 viperlib.snprintf(buf, 64, "%a", v1) testcheck.info(buf) testcheck.check(string.strcmp(buf, "0x1p+0") == 0, "%a 1.0 -> 0x1p+0", "FAIL 1.0") v2: double = 2.0 viperlib.snprintf(buf, 64, "%a", v2) testcheck.info(buf) testcheck.check(string.strcmp(buf, "0x1p+1") == 0, "%a 2.0 -> 0x1p+1", "FAIL 2.0") v_half: double = 0.5 viperlib.snprintf(buf, 64, "%a", v_half) testcheck.info(buf) testcheck.check(string.strcmp(buf, "0x1p-1") == 0, "%a 0.5 -> 0x1p-1", "FAIL 0.5") v3: double = 3.0 viperlib.snprintf(buf, 64, "%a", v3) testcheck.info(buf) testcheck.check(string.strcmp(buf, "0x1.8p+1") == 0, "%a 3.0 -> 0x1.8p+1", "FAIL 3.0") v_pi: double = 3.14 viperlib.snprintf(buf, 64, "%a", v_pi) testcheck.info(buf) testcheck.check(string.strcmp(buf, "0x1.91eb851eb851fp+1") == 0, "%a 3.14 -> 0x1.91eb851eb851fp+1", "FAIL 3.14") v0: double = 0.0 viperlib.snprintf(buf, 64, "%a", v0) testcheck.info(buf) testcheck.check(string.strcmp(buf, "0x0p+0") == 0, "%a 0.0 -> 0x0p+0", "FAIL 0.0") vn1: double = -1.0 viperlib.snprintf(buf, 64, "%a", vn1) testcheck.info(buf) testcheck.check(string.strcmp(buf, "-0x1p+0") == 0, "%a -1.0 -> -0x1p+0", "FAIL -1.0") # ---------- uppercase %A ---------- testcheck.section("uppercase %A") viperlib.snprintf(buf, 64, "%A", v_pi) testcheck.info(buf) testcheck.check(string.strcmp(buf, "0X1.91EB851EB851FP+1") == 0, "%A 3.14 -> 0X1...P+1", "FAIL %A") # ---------- # flag forces radix point ---------- testcheck.section("# flag") viperlib.snprintf(buf, 64, "%#a", v1) testcheck.info(buf) testcheck.check(string.strcmp(buf, "0x1.p+0") == 0, "%#a 1.0 -> 0x1.p+0", "FAIL %#a") # ---------- explicit precision 13 (no trimming) ---------- testcheck.section("precision 13") viperlib.snprintf(buf, 64, "%.13a", v1) testcheck.info(buf) testcheck.check(string.strcmp(buf, "0x1.0000000000000p+0") == 0, "%.13a 1.0 -> 0x1.0000000000000p+0", "FAIL %.13a") # ---------- width/alignment ---------- testcheck.section("width/align") viperlib.snprintf(buf, 64, "%10a", v1) testcheck.info(buf) testcheck.check(string.strcmp(buf, " 0x1p+0") == 0, "%10a 1.0 -> ' 0x1p+0'", "FAIL %10a") viperlib.snprintf(buf, 64, "%-10a|", v1) testcheck.info(buf) testcheck.check(string.strcmp(buf, "0x1p+0 |") == 0, "%-10a 1.0 -> '0x1p+0 |'", "FAIL %-10a") # ---------- show_sign ---------- testcheck.section("show_sign") viperlib.snprintf(buf, 64, "%+a", v1) testcheck.info(buf) testcheck.check(string.strcmp(buf, "+0x1p+0") == 0, "%+a 1.0 -> +0x1p+0", "FAIL %+a") # ---------- inf / nan ---------- testcheck.section("inf/nan") # Build +inf from bit pattern 0x7FF0000000000000 inf_bits: uint64 = 0x7FF0000000000000 inf_val: double = 0.0 *(&inf_val) = *(&inf_bits) viperlib.snprintf(buf, 64, "%a", inf_val) testcheck.info(buf) testcheck.check(string.strcmp(buf, "inf") == 0, "%a +inf -> inf", "FAIL +inf") # Build -inf from bit pattern 0xFFF0000000000000 ninf_bits: uint64 = 0xFFF0000000000000 ninf_val: double = 0.0 *(&ninf_val) = *(&ninf_bits) viperlib.snprintf(buf, 64, "%a", ninf_val) testcheck.info(buf) testcheck.check(string.strcmp(buf, "-inf") == 0, "%a -inf -> -inf", "FAIL -inf") # Build nan from bit pattern 0x7FF8000000000000 nan_bits: uint64 = 0x7FF8000000000000 nan_val: double = 0.0 *(&nan_val) = *(&nan_bits) viperlib.snprintf(buf, 64, "%a", nan_val) testcheck.info(buf) testcheck.check(string.strcmp(buf, "nan") == 0, "%a nan -> nan", "FAIL nan") # Uppercase INF/NAN viperlib.snprintf(buf, 64, "%A", inf_val) testcheck.info(buf) testcheck.check(string.strcmp(buf, "INF") == 0, "%A +inf -> INF", "FAIL INF") # viperlib %g/%f/%e diagnosis: %a is correct but %.17g prints a wrong value. # Same double read twice from one va_list: %.17g wrong, %a right. testcheck.section("viperlib %g bug diagnosis") v_pi17: double = 3.1415926535897931 v_e17: double = 2.7182818284590455 # T1: same arg via %.17g vs %a (repro; %a correct means value is fine) viperlib.snprintf(buf, 128, "T1 %.17g|%a", v_pi17, v_pi17) testcheck.info(buf) # T2: two different doubles with %.17g viperlib.snprintf(buf, 128, "T2 %.17g|%.17g", v_pi17, v_e17) testcheck.info(buf) # T3: CRT reference stdio.snprintf(buf, 128, "T3 %.17g|%a", v_pi17, v_pi17) testcheck.info(buf) # T4: different precisions viperlib.snprintf(buf, 128, "T4 %.17g|%.10g|%.6g|%g", v_pi17, v_pi17, v_pi17, v_pi17) testcheck.info(buf) # T5: %f / %e viperlib.snprintf(buf, 128, "T5 %.17f|%.17e", v_pi17, v_pi17) testcheck.info(buf) # T6: exact values (1.0/0.5); wrong here means arithmetic layer broken viperlib.snprintf(buf, 128, "T6 %.17g|%.17g|%.17g", 1.0, 0.5, 123.456) testcheck.info(buf) # T7/T8: double at different vararg positions (va_list slot dependence) viperlib.snprintf(buf, 128, "T7 %d|%.17g|%d", 100, v_pi17, 200) testcheck.info(buf) viperlib.snprintf(buf, 128, "T8 %d|%d|%.17g", 100, 200, v_pi17) testcheck.info(buf) # T9: literals passed directly (checks passing path) viperlib.snprintf(buf, 128, "T9 %.17g|%a", 3.1415926535897931, 3.1415926535897931) testcheck.info(buf) # T10: reversed order, %a before %.17g viperlib.snprintf(buf, 128, "T10 %a|%.17g", v_pi17, v_pi17) testcheck.info(buf) # Multi-int varargs repro: with 3+ int args, viperlib.snprintf reads garbage # at the 2nd %d (va_arg cursor advances wrong past the register area). testcheck.section("multi-int varargs repro") # R1: 3 const ints (reproduces DefineTest MAX_SIZE/PAGE/MASK pattern) viperlib.snprintf(buf, 128, "R1 %d|%d|%d", 1024, 4096, 255) testcheck.info(buf) testcheck.check(string.strcmp(buf, "R1 1024|4096|255") == 0, "R1 3-const-int (1024|4096|255)", "R1 FAIL (第2变参被误读)") # R2: 3 runtime int vars (rules out constant folding) ia: int = 1024 ib: int = 4096 ic: int = 255 viperlib.snprintf(buf, 128, "R2 %d|%d|%d", ia, ib, ic) testcheck.info(buf) testcheck.check(string.strcmp(buf, "R2 1024|4096|255") == 0, "R2 3-var-int (1024|4096|255)", "R2 FAIL (第2变参被误读)") # R3: 4 varargs (further exposes cursor advance) viperlib.snprintf(buf, 128, "R3 %d|%d|%d|%d", 1, 2, 3, 4) testcheck.info(buf) testcheck.check(string.strcmp(buf, "R3 1|2|3|4") == 0, "R3 4-int (1|2|3|4)", "R3 FAIL (变参游标推进错误)") # R4: 2nd vararg is 0 (exposes truncated reads) viperlib.snprintf(buf, 128, "R4 %d|%d|%d", 100, 0, 200) testcheck.info(buf) testcheck.check(string.strcmp(buf, "R4 100|0|200") == 0, "R4 middle-zero (100|0|200)", "R4 FAIL (第2变参被误读)") stdlib.free(buf) return 0