bigywater.tech
ClickHouse 内部原理 · 51

Decimal:金额计算为什么不能用 Float

真实对比Float64和Decimal64在单次和100万次累加下的计算精度差异,并诚实记录Decimal32加法和乘法溢出检查的真实不对称行为。

100万次累加见真章
真实Float64累积出10000.000000000087的误差
加法乘法不对称
真实Decimal32加法容许超位数,乘法立刻报错

做的事:真实对比同样的计算,Float64和Decimal64的结果

代码SELECT toFloat64(0.1) + toFloat64(0.2); SELECT toDecimal64(0.1, 4) + toDecimal64(0.2, 4); -- 真实放大100万倍:sum(0.01) 累加100万次 SELECT sum(toFloat64(0.01)) FROM numbers(1000000); SELECT sum(toDecimal64(0.01, 4)) FROM numbers(1000000);

验证:真实的浮点误差,累加100万次之后清晰可见

实测 · 本机执行输出
Float64:   toFloat64(0.1) + toFloat64(0.2)   → 0.30000000000000004   ← 真实的浮点误差
Decimal64: toDecimal64(0.1,4) + toDecimal64(0.2,4) → 0.3            ← 真实精确

sum(toFloat64(0.01))     FROM numbers(1000000) → 10000.000000000087   ← 真实累积误差
sum(toDecimal64(0.01,4)) FROM numbers(1000000) → 10000                ← 真实依然精确

单次0.1+0.2的误差(多出来的...004)看起来微不足道,但真实累加100万次之后,Float64的结果真实偏离了精确值0.000000000087——这个误差本身虽然还很小,但它真实证明了浮点数误差是会累积的,业务金额计算里如果反复做加减法(比如订单流水的滚动汇总),误差会真实随着计算次数增长,而Decimal64全程保持精确。

诚实的意外:Decimal的溢出检查,加法和乘法真实不一样严格

实测 · 本机执行输出(Decimal32理论上限是9位数:999999999)
SELECT toDecimal32(999999999, 0) + toDecimal32(1, 0) AS r, toTypeName(r);
→ 1000000000   Decimal(9, 0)     ← 真实没有报错,结果变成了10位数

SELECT toDecimal32(999999999, 0) * toDecimal32(999999999, 0);
→ DB::Exception: Decimal math overflow: ...(DECIMAL_OVERFLOW)   ← 真实立刻报错

SELECT toDecimal64(1.23,2) * toDecimal64(4.56,2) AS r, toTypeName(r);
→ 5.5965   Decimal(18, 4)   ← 真实乘法自动把小数位扩展成了2+2=4位
这组真实测试暴露了一个容易被"Decimal32理论上限是999999999(9位数)"这句话误导的地方:加法真实允许结果超出这个名义上限而不报错(本文的999999999+1真实变成了10位数的1000000000,依然正常返回),但乘法在结果真的超出内部实际存储能力时会真实报DECIMAL_OVERFLOW。真实合理的解释是:Decimal的加法在ClickHouse内部会按更宽的中间类型做计算再转换回结果类型,容许一定的进位空间;乘法的结果位数增长得更快(两个9位数相乘理论上需要18位数才能精确表示),当真实超出这个更大的安全边界时才会报错。乘法本身的行为也印证了Decimal类型"自动扩展精度"的真实机制——本文1.23×4.56这两个小数位是2的Decimal64相乘,结果真实自动变成了小数位4(2+2)的Decimal(18,4),不需要手动指定精度。真实的建议是:不要死记"Decimal32=9位数"这类数字,业务真正在意精确边界的场景,最好显式选择留有余量的Decimal64/Decimal128,而不是精打细算贴着Decimal32的理论上限设计。

分步演示:从浮点误差到Decimal溢出检查的真实不对称

每一步都是本文上面用真实计算跑出来的原始结果。

Float真实累积误差,Decimal的溢出检查真实不对称
这一步新验证的场景

    本文用到的工具

    • 本机 ClickHouse 26.9.4(单节点),真实对比Float64与Decimal64/Decimal32的计算精度
    京ICP备2026067502号-1京公网安备11010502063487号