真实对比Float64和Decimal64在单次和100万次累加下的计算精度差异,并诚实记录Decimal32加法和乘法溢出检查的真实不对称行为。
代码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);
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全程保持精确。
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位
每一步都是本文上面用真实计算跑出来的原始结果。