fix(includes): 修复浮点常量精度截断与 viperlib %g 小数反转 - ConstFloat 浮点常量文本改 %.17e(全精度+LLVM合法语法, 修复 %e 6位小数截断致 llc 值错与 %.17g 裸整数报错); viperlib %g 小数位改逆序存储(原三变量交换循环在 TPV 编译产物中失效, 小数位反转 456->654); SprintfTest 增加 %g 诊断用例 T1-T10

This commit is contained in:
TermiNexus committed 2026-08-08 20:38:25 +08:00
1 parent 61e578fe9a
commit 9ee519e998
3 files changed
+98 -44

No files matched your search

+67 -18
View File
@@ -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
+9 -2
View File
@@ -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
+22 -24
View File
@@ -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: