Java 中带双精度的模数
在使用双精度时,你如何处理Java在模运算符上的奇怪行为?
例如,你会期望结果是(事实上,谷歌说我不会发疯),但是当我在Java中运行它时,我得到了。3.9 - (3.9 % 0.1)
3.9
3.8000000000000003
我知道这是Java存储和处理方式加倍的结果,但是有没有办法解决这个问题呢?
在使用双精度时,你如何处理Java在模运算符上的奇怪行为?
例如,你会期望结果是(事实上,谷歌说我不会发疯),但是当我在Java中运行它时,我得到了。3.9 - (3.9 % 0.1)
3.9
3.8000000000000003
我知道这是Java存储和处理方式加倍的结果,但是有没有办法解决这个问题呢?
如果您需要精确的结果,请使用精确类型:
double val = 3.9 - (3.9 % 0.1);
System.out.println(val); // 3.8000000000000003
BigDecimal x = new BigDecimal( "3.9" );
BigDecimal bdVal = x.subtract( x.remainder( new BigDecimal( "0.1" ) ) );
System.out.println(bdVal); // 3.9
为什么是 3.8000...003?因为Java使用FPU来计算结果。3.9不可能完全存储在IEEE双精度表示法中,因此它存储3.89999...相反。而3.8999%0.01给出0.09999...因此,结果比3.8大一点。
来自 Java 语言规范:
由 % 运算符计算的浮点余数运算的结果与 IEEE 754 定义的余数运算所产生的结果不同。IEEE 754 余数操作从舍入除法(而不是截断除法)计算余数,因此其行为与通常的整数余数运算符的行为不同。相反,Java编程语言在浮点运算上定义了%,其行为方式类似于整数余数运算符的行为;这可以与C库函数fmod进行比较。IEEE 754 余数运算可由库例程 Math.IEEEremainder 计算。
换句话说,这是因为Java舍入了计算余数所涉及的除法结果,而IEEE754指定截断除法的答案。这个特殊案例似乎非常清楚地暴露了这种差异。
您可以使用Math.IEEEremainder获得您期望的答案:
System.out.println(3.9 - (3.9 % 0.1));
System.out.println(3.9 - Math.IEEEremainder(3.9, 0.1));