昨天同事对账,金额列显示成 12.300000000000001,客服截图过来问是不是算错了,其实就是浮点数那点老毛病。改法有好几种,用错场景照样出错。

最省事的是 ToString 带格式串,直接写 F 加数字:

double a = 12.3456; string s = a.ToString(F2);

F2 就是固定两位并四舍五入。它走的是哪种舍入要看运行时版本,金额场景别指望它完全符合财务规则。

Math.Round 和格式化的结果会不一样

Math.Round 传两个参数时默认走银行家舍入,遇上五结尾会往偶数靠,12.345 会变成 12.34 而不是 12.35。要普通四舍五入必须补第三个参数:

Math.Round(12.345, 2, MidpointRounding.AwayFromZero)

做财务、开发票的,AwayFromZero 这个参数一个字母都不能少,否则对账永远差一分钱。

金额本来就该用 decimal 而不是 double

double 存 0.1 本身就是近似值,累加多了误差就冒出来。金额、单价、税率这些字段,数据库定 decimal(18,2),C# 里也用 decimal,别图省事用 double。decimal 加上格式串零杠零,也就是两个零加小数点,才是最稳的显示写法。

  • 只显示用:ToString 带格式串,或者补零到两位
  • 要参与运算:用 decimal,配合 Math.Round 加 AwayFromZero
  • 序列化成 JSON:decimal 会带一堆尾零,前端再用 toFixed 兜一层

还有个小细节,格式串用 N 开头会加千分位逗号,1234.56 显示成一二三四点五六带逗号,看着舒服,但要拿去当数字用就得先去逗号。

调试的时候还有一招,把值先转成字符串再肉眼确认,比断点里盯着科学计数法看清楚多了。格式串最好在项目里统一约定成一个工具方法,全项目都调它,免得有人用格式化有人用 Round,等到对不上账再拍脑袋猜是哪一步错了。

一句话:显示归显示,计算归计算,钱的事一律 decimal 起步,double 只配做科学计算。