15

在使用双精度数时,您如何处理 Java 的模数运算符的怪异行为?

例如,您会期望结果3.9 - (3.9 % 0.1)3.9(事实上,Google说我不会发疯),但是当我在 Java 中运行它时,我得到3.8000000000000003.

我知道这是 Java 存储和处理方式加倍的结果,但是有没有办法解决它?

4

4 回答 4

20

如果您需要精确的结果,请使用精确类型:

    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 大一点。

于 2010-07-12T09:47:26.930 回答
5

来自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));
于 2010-07-12T11:07:08.157 回答
2

您可以使用 java.math.BigDecimal 及其方法divideAndRemainder ()。

于 2010-07-12T09:46:39.087 回答
1

如果您知道要处理的小数位数,则可以先尝试转换为整数。这只是浮点不准确的典型案例。而不是做3.9 % 0.1你正在做类似的事情3.899 % 0.0999

于 2010-07-12T09:45:25.070 回答