仅存储不做运算时,Java double类型存储货币值是否总能保持精确?
关于double存储0.00~2.00区间货币值的精度问题解答
核心结论
- 「0.00到2.00区间的货币值用double存储100%精确」这个结论不成立
- 直接执行
double v = 0.01赋值操作确实存在精度问题
具体原因
- double的存储机制决定了它无法精确表示大多数十进制小数
double是遵循IEEE 754标准的双精度二进制浮点数,本质是用「整数×2的整数次幂」的形式存储数值。而0.01这类两位小数对应的是十进制分数1/100,分母包含质因数5,无法拆解成2的整数次幂的组合,因此从原理上就不可能被二进制浮点数精确表示。你赋值的那一刻,double里存的就是和0.01非常接近的近似值,而非精确的0.01。
你可以用这段代码直接查看double存储的真实值:
// 直接把double转成无舍入的BigDecimal输出,就能看到完整的存储内容 System.out.println(new BigDecimal(0.01)); // 实际输出:0.01000000000000000020816681711721685132943093776702880859375
- 测试输出的全精确结果是格式化工具的假象
你使用的DecimalFormat默认采用HALF_EVEN(银行家舍入)模式,你设置的小数点后25位的输出规则,刚好把0~2区间两位小数的微小误差给舍入掉了,所以看不到后面的非零位,并非这些值真的存储精确。 - 为什么0~2区间的误差看起来不明显?
双精度浮点数有53位有效二进制位,对应约15~17位十进制有效数字。小于2的数整数部分只占1位二进制位,剩下的位数都用来表示小数部分,所以两位十进制小数的近似误差会非常小,仅做存储、不做运算、且格式化输出位数不足的情况下,很难感知到误差的存在。但这不代表误差不存在,只要对这些值做累加、乘法等运算,误差会快速累积,最终出现明显的计算错误。
场景建议
如果必须对接强制要求用浮点数传输的系统,建议你本地始终用Long(单位:分)或者BigDecimal存储、计算货币值,仅在给外部系统传值的最后一步转成double,接收外部系统的double值后第一时间转回精确类型处理,中间全程不要用double做任何货币运算,能最大程度规避精度问题。
内容的提问来源于stack exchange,提问作者Dreamspace President
相关产品推荐
相关产品推荐

