From 9ee519e9988d9bb77c6d25c02df5ae4683b73e9f Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 8 Aug 2026 20:38:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(includes):=20=E4=BF=AE=E5=A4=8D=E6=B5=AE?= =?UTF-8?q?=E7=82=B9=E5=B8=B8=E9=87=8F=E7=B2=BE=E5=BA=A6=E6=88=AA=E6=96=AD?= =?UTF-8?q?=E4=B8=8E=20viperlib=20%g=20=E5=B0=8F=E6=95=B0=E5=8F=8D?= =?UTF-8?q?=E8=BD=AC=20-=20ConstFloat=20=E6=B5=AE=E7=82=B9=E5=B8=B8?= =?UTF-8?q?=E9=87=8F=E6=96=87=E6=9C=AC=E6=94=B9=20%.17e(=E5=85=A8=E7=B2=BE?= =?UTF-8?q?=E5=BA=A6+LLVM=E5=90=88=E6=B3=95=E8=AF=AD=E6=B3=95,=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20%e=206=E4=BD=8D=E5=B0=8F=E6=95=B0=E6=88=AA=E6=96=AD?= =?UTF-8?q?=E8=87=B4=20llc=20=E5=80=BC=E9=94=99=E4=B8=8E=20%.17g=20?= =?UTF-8?q?=E8=A3=B8=E6=95=B4=E6=95=B0=E6=8A=A5=E9=94=99);=20viperlib=20%g?= =?UTF-8?q?=20=E5=B0=8F=E6=95=B0=E4=BD=8D=E6=94=B9=E9=80=86=E5=BA=8F?= =?UTF-8?q?=E5=AD=98=E5=82=A8(=E5=8E=9F=E4=B8=89=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E4=BA=A4=E6=8D=A2=E5=BE=AA=E7=8E=AF=E5=9C=A8=20TPV=20=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E4=BA=A7=E7=89=A9=E4=B8=AD=E5=A4=B1=E6=95=88,=20?= =?UTF-8?q?=E5=B0=8F=E6=95=B0=E4=BD=8D=E5=8F=8D=E8=BD=AC=20456->654);=20Sp?= =?UTF-8?q?rintfTest=20=E5=A2=9E=E5=8A=A0=20%g=20=E8=AF=8A=E6=96=AD?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=20T1-T10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Test/SprintfTest/App/main.vp | 85 +++++++++++++++++++++++++++-------- includes/llvmlite/__values.vp | 11 ++++- includes/viperlib.vp | 46 +++++++++---------- 3 files changed, 98 insertions(+), 44 deletions(-) diff --git a/Test/SprintfTest/App/main.vp b/Test/SprintfTest/App/main.vp index 7ae39c0..214e594 100644 --- a/Test/SprintfTest/App/main.vp +++ b/Test/SprintfTest/App/main.vp @@ -4,6 +4,7 @@ import stdio import stdlib import testcheck import viperlib +import string # ============================================================ @@ -13,7 +14,7 @@ import viperlib @t.CExport def main() -> int: testcheck.begin("SprintfTest: %a/%A runtime verification") - buf: bytes = stdlib.malloc(64) + buf: bytes = stdlib.malloc(256) # ---------- 默认精度(核心正确性) ---------- testcheck.section("default precision") @@ -21,71 +22,71 @@ def main() -> int: v1: t.CDouble = 1.0 viperlib.snprintf(buf, 64, "%a", v1) testcheck.info(buf) - testcheck.check(buf == "0x1p+0", "%a 1.0 -> 0x1p+0", "FAIL 1.0") + testcheck.check(string.strcmp(buf, "0x1p+0") == 0, "%a 1.0 -> 0x1p+0", "FAIL 1.0") v2: t.CDouble = 2.0 viperlib.snprintf(buf, 64, "%a", v2) testcheck.info(buf) - testcheck.check(buf == "0x1p+1", "%a 2.0 -> 0x1p+1", "FAIL 2.0") + testcheck.check(string.strcmp(buf, "0x1p+1") == 0, "%a 2.0 -> 0x1p+1", "FAIL 2.0") v_half: t.CDouble = 0.5 viperlib.snprintf(buf, 64, "%a", v_half) testcheck.info(buf) - testcheck.check(buf == "0x1p-1", "%a 0.5 -> 0x1p-1", "FAIL 0.5") + testcheck.check(string.strcmp(buf, "0x1p-1") == 0, "%a 0.5 -> 0x1p-1", "FAIL 0.5") v3: t.CDouble = 3.0 viperlib.snprintf(buf, 64, "%a", v3) testcheck.info(buf) - testcheck.check(buf == "0x1.8p+1", "%a 3.0 -> 0x1.8p+1", "FAIL 3.0") + testcheck.check(string.strcmp(buf, "0x1.8p+1") == 0, "%a 3.0 -> 0x1.8p+1", "FAIL 3.0") v_pi: t.CDouble = 3.14 viperlib.snprintf(buf, 64, "%a", v_pi) testcheck.info(buf) - testcheck.check(buf == "0x1.91eb851eb851fp+1", "%a 3.14 -> 0x1.91eb851eb851fp+1", "FAIL 3.14") + testcheck.check(string.strcmp(buf, "0x1.91eb851eb851fp+1") == 0, "%a 3.14 -> 0x1.91eb851eb851fp+1", "FAIL 3.14") v0: t.CDouble = 0.0 viperlib.snprintf(buf, 64, "%a", v0) testcheck.info(buf) - testcheck.check(buf == "0x0p+0", "%a 0.0 -> 0x0p+0", "FAIL 0.0") + testcheck.check(string.strcmp(buf, "0x0p+0") == 0, "%a 0.0 -> 0x0p+0", "FAIL 0.0") vn1: t.CDouble = -1.0 viperlib.snprintf(buf, 64, "%a", vn1) testcheck.info(buf) - testcheck.check(buf == "-0x1p+0", "%a -1.0 -> -0x1p+0", "FAIL -1.0") + testcheck.check(string.strcmp(buf, "-0x1p+0") == 0, "%a -1.0 -> -0x1p+0", "FAIL -1.0") # ---------- 大写 %A ---------- testcheck.section("uppercase %A") viperlib.snprintf(buf, 64, "%A", v_pi) testcheck.info(buf) - testcheck.check(buf == "0X1.91EB851EB851FP+1", "%A 3.14 -> 0X1...P+1", "FAIL %A") + testcheck.check(string.strcmp(buf, "0X1.91EB851EB851FP+1") == 0, "%A 3.14 -> 0X1...P+1", "FAIL %A") # ---------- # flag 强制小数点 ---------- testcheck.section("# flag") viperlib.snprintf(buf, 64, "%#a", v1) testcheck.info(buf) - testcheck.check(buf == "0x1.p+0", "%#a 1.0 -> 0x1.p+0", "FAIL %#a") + testcheck.check(string.strcmp(buf, "0x1.p+0") == 0, "%#a 1.0 -> 0x1.p+0", "FAIL %#a") # ---------- 显式精度 13(不裁剪) ---------- testcheck.section("precision 13") viperlib.snprintf(buf, 64, "%.13a", v1) testcheck.info(buf) - testcheck.check(buf == "0x1.0000000000000p+0", "%.13a 1.0 -> 0x1.0000000000000p+0", "FAIL %.13a") + testcheck.check(string.strcmp(buf, "0x1.0000000000000p+0") == 0, "%.13a 1.0 -> 0x1.0000000000000p+0", "FAIL %.13a") # ---------- 宽度/对齐 ---------- testcheck.section("width/align") viperlib.snprintf(buf, 64, "%10a", v1) testcheck.info(buf) - testcheck.check(buf == " 0x1p+0", "%10a 1.0 -> ' 0x1p+0'", "FAIL %10a") + 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(buf == "0x1p+0 |", "%-10a 1.0 -> '0x1p+0 |'", "FAIL %-10a") + 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(buf == "+0x1p+0", "%+a 1.0 -> +0x1p+0", "FAIL %+a") + testcheck.check(string.strcmp(buf, "+0x1p+0") == 0, "%+a 1.0 -> +0x1p+0", "FAIL %+a") # ---------- inf / nan ---------- testcheck.section("inf/nan") @@ -96,7 +97,7 @@ def main() -> int: c.Load(c.Addr(inf_val), c.Addr(inf_bits)) viperlib.snprintf(buf, 64, "%a", inf_val) testcheck.info(buf) - testcheck.check(buf == "inf", "%a +inf -> inf", "FAIL +inf") + testcheck.check(string.strcmp(buf, "inf") == 0, "%a +inf -> inf", "FAIL +inf") # 构造 -inf: bit pattern 0xFFF0000000000000 ninf_bits: t.CUInt64T = 0xFFF0000000000000 @@ -104,7 +105,7 @@ def main() -> int: c.Load(c.Addr(ninf_val), c.Addr(ninf_bits)) viperlib.snprintf(buf, 64, "%a", ninf_val) testcheck.info(buf) - testcheck.check(buf == "-inf", "%a -inf -> -inf", "FAIL -inf") + testcheck.check(string.strcmp(buf, "-inf") == 0, "%a -inf -> -inf", "FAIL -inf") # 构造 nan: bit pattern 0x7FF8000000000000 nan_bits: t.CUInt64T = 0x7FF8000000000000 @@ -112,12 +113,60 @@ def main() -> int: c.Load(c.Addr(nan_val), c.Addr(nan_bits)) viperlib.snprintf(buf, 64, "%a", nan_val) testcheck.info(buf) - testcheck.check(buf == "nan", "%a nan -> nan", "FAIL nan") + testcheck.check(string.strcmp(buf, "nan") == 0, "%a nan -> nan", "FAIL nan") # 大写 INF/NAN viperlib.snprintf(buf, 64, "%A", inf_val) testcheck.info(buf) - testcheck.check(buf == "INF", "%A +inf -> INF", "FAIL INF") + testcheck.check(string.strcmp(buf, "INF") == 0, "%A +inf -> INF", "FAIL INF") + + # ============================================================ + # viperlib %g/%f/%e 浮点格式化诊断(定位 SimdTest 浮点 CDefine 值错) + # 已知现象: %a 正确, %.17g 输出 3.1397985356295141 (应为 3.1415926535897931) + # 同一 va_list 连续读同一 double, %.17g 错而 %a 对 → 定位是算法还是传参/读取 + # ============================================================ + testcheck.section("viperlib %g bug diagnosis") + + v_pi17: t.CDouble = 3.1415926535897931 + v_e17: t.CDouble = 2.7182818284590455 + + # T1: 同一参数 %.17g vs %a(复现;%a 对则说明值本身正确) + viperlib.snprintf(buf, 128, "T1 %.17g|%a", v_pi17, v_pi17) + testcheck.info(buf) + + # T2: 两个 %.17g 不同值 + viperlib.snprintf(buf, 128, "T2 %.17g|%.17g", v_pi17, v_e17) + testcheck.info(buf) + + # T3: CRT 对照 + stdio.snprintf(buf, 128, "T3 %.17g|%a", v_pi17, v_pi17) + testcheck.info(buf) + + # T4: 不同精度 + 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: 简单精确值(1.0/0.5 二进制精确, 若这些也错则算术层坏) + viperlib.snprintf(buf, 128, "T6 %.17g|%.17g|%.17g", 1.0, 0.5, 123.456) + testcheck.info(buf) + + # T7/T8: double 在变参不同位置(va_list 读取位置相关性) + 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: 字面量直接传(非常量折叠则看传递路径) + viperlib.snprintf(buf, 128, "T9 %.17g|%a", 3.1415926535897931, 3.1415926535897931) + testcheck.info(buf) + + # T10: 顺序反转, %a 在前 %.17g 在后 + viperlib.snprintf(buf, 128, "T10 %a|%.17g", v_pi17, v_pi17) + testcheck.info(buf) stdlib.free(buf) return 0 diff --git a/includes/llvmlite/__values.vp b/includes/llvmlite/__values.vp index 02af208..a2767b0 100644 --- a/includes/llvmlite/__values.vp +++ b/includes/llvmlite/__values.vp @@ -2,6 +2,7 @@ import t, c from stdint import * import string import viperlib +import stdio import memhub from linkedlist import GSListNode from .__types import LLVMType, TypePrint @@ -95,10 +96,16 @@ def ConstFloat(pool: memhub.MemBuddy | t.CPtr, ty: LLVMType | t.CPtr, v.IsConst = 1 v.FloatVal = val v.IntVal = 0 - # 格式化名字: LLVM IR 使用 "%e" 科学计数法(6 位小数) + # 格式化名字: LLVM IR 浮点常量文本 + # 必须用 %.17e 保证 double 精度(17 位小数)且恒为科学计数法(带小数点), + # LLVM 语法要求浮点常量不能是裸整数(%.17g 对 1.0 输出 "1" 会报 + # "integer constant must have integer type")。原 %e 默认仅 6 位小数, + # 常量被截断(3.1415926535897931 -> "3.141593e+00",llc 解析后值错, + # 导致浮点 CDefine/字面量生成错误 LLVM 值,llc 报 ret i32 0)。 + # 用 CRT stdio.snprintf: viperlib 自实现 %g 有小数位反转 bug。 name_buf: t.CChar | t.CPtr = pool.alloc(32) if name_buf is not None: - viperlib.snprintf(name_buf, 32, "%e", val) + stdio.snprintf(name_buf, 32, "%.17e", val) v.Name = name_buf return v diff --git a/includes/viperlib.vp b/includes/viperlib.vp index f1d9c69..144fd65 100644 --- a/includes/viperlib.vp +++ b/includes/viperlib.vp @@ -1009,19 +1009,18 @@ def vsnprintf(buf: t.CChar | t.CPtr, size: t.CSizeT, fmt: str, ap: t.CChar | t.C if prec_e > 0: temp[1] = 46 g_len = 2 + # 小数位逆序存入(个位放 g_len+prec_e-1),temp 直接为正序。 + # 原交换反转循环在 TPV 编译产物中失效(小数位未反转,如 456->654), + # 改逆序存储,与 %f 的倒序输出一致,绕开三变量交换。 fi: t.CInt = 0 - while fi < prec_e and g_len < TEMP_BUF_SIZE - 1: - temp[g_len] = t.CChar(frac_p % 10 + 48) - g_len += 1 - frac_p //= 10 - lo: t.CInt = 2 - hi: t.CInt = g_len - 1 - while lo < hi: - tc: t.CChar = temp[lo] - temp[lo] = temp[hi] - temp[hi] = tc - lo += 1 - hi -= 1 + fp_inv: t.CUInt64T = frac_p + fp_idx: t.CInt = g_len + prec_e - 1 + while fi < prec_e and fp_idx < TEMP_BUF_SIZE - 1: + temp[fp_idx] = t.CChar(fp_inv % 10 + 48) + fp_idx -= 1 + fp_inv //= 10 + fi += 1 + g_len += prec_e else: prec_f: t.CInt = precision - 1 - exp10 if prec_f < 0: prec_f = 0 @@ -1049,19 +1048,18 @@ def vsnprintf(buf: t.CChar | t.CPtr, size: t.CSizeT, fmt: str, ap: t.CChar | t.C frac_val *= 10.0 p += 1 frac_part: t.CUInt64T = t.CUInt64T(frac_val + 0.5) + # 小数位逆序存入(个位放 g_len+prec_f-1),temp 直接为正序。 + # 原交换反转循环在 TPV 编译产物中失效(小数位未反转,如 456->654), + # 改逆序存储,与 %f 的倒序输出一致,绕开三变量交换。 fi = 0 - while fi < prec_f and g_len < TEMP_BUF_SIZE - 1: - temp[g_len] = t.CChar(frac_part % 10 + 48) - g_len += 1 - frac_part //= 10 - lo = g_len - prec_f - hi = g_len - 1 - while lo < hi: - tc = temp[lo] - temp[lo] = temp[hi] - temp[hi] = tc - lo += 1 - hi -= 1 + fp_inv: t.CUInt64T = frac_part + fp_idx: t.CInt = g_len + prec_f - 1 + while fi < prec_f and fp_idx < TEMP_BUF_SIZE - 1: + temp[fp_idx] = t.CChar(fp_inv % 10 + 48) + fp_idx -= 1 + fp_inv //= 10 + fi += 1 + g_len += prec_f if not alt_form and g_len > 0: while g_len > 0: if temp[g_len - 1] == 48: