JavaSE 1.8中Double类型算术运算精度异常问题问询
Double类型算术运算精度异常问题分析与解决方案
我完全懂这种困惑——明明是简单的减法操作,结果却跳出个奇怪的精度偏差,临时用Math.round()凑合用虽然能解决眼前问题,但总觉得这事儿不该这么麻烦。咱们好好捋捋这个问题:
问题根源:不是Java的锅,是浮点数的固有特性
这绝对不是Java运行时引擎的bug,而是double这类浮点数在计算机中存储的本质导致的。double遵循IEEE 754标准,用64位二进制来表示十进制小数,但很多十进制小数(比如你遇到的0.41)根本无法被二进制浮点数精确表达,只能存储一个无限接近的近似值。
你看到的2072.41在内存里其实是一个非常接近但严格不等于2072.41的二进制近似值,减去能被精确表示的1900.0之后,原本隐藏的微小误差就被放大显现出来,于是得到了172.40999995这种看起来“离谱”的结果。
能不能复现?当然可以
只要是遵循IEEE 754标准的浮点数实现(几乎所有现代编程语言、运行环境都遵循这个标准),都会出现类似的精度问题。你在命令行编译运行能复现,恰恰说明这是普遍现象,和Eclipse没什么关系。
要不要换原生数据类型?得看场景
首先纠正一下:double本身就是Java的原生数据类型,但如果你的场景是货币、金额这类对精度要求极高的业务,double和float都不是合适的选择。更靠谱的方案有两个:
- 用
BigDecimal精确计算:它专门用来处理需要精确十进制表示的数值,完全规避浮点精度问题。用法很简单:
划重点:一定要用字符串构造方法,别用BigDecimal balance = new BigDecimal("2072.41"); BigDecimal allocation = new BigDecimal("1900.0"); balance = balance.subtract(allocation); // 结果就是精确的172.41double构造,不然还是会带入近似值的坑。 - 以整数形式存储金额:如果业务允许,把金额以分为单位(比如207241分代替2072.41元),用
long类型存储和运算,都是整数操作自然不会有精度问题,展示的时候再转换成元即可。
关于你的临时解决方案
Math.round(dblBalance * 100.0)/100.0确实能在大多数场景下得到预期结果,但它本质上是对近似值的“事后修正”,如果是连续多次运算的场景,误差还是可能累积出问题。如果是金额相关的业务,真心建议换成上面提到的两种方案。
内容的提问来源于stack exchange,提问作者Harold Rosenkrans
相关产品推荐
相关产品推荐

