Java 中的双精度模数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3227342/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 00:53:11  来源:igfitidea点击:

Modulus with doubles in Java

javadoublemodulus

提问by Andy

How do you deal with Java's weird behaviour with the modulus operator when using doubles?

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

For example, you would expect the result of 3.9 - (3.9 % 0.1)to be 3.9(and indeed, Googlesays I'm not going crazy), but when I run it in Java I get 3.8000000000000003.

例如,你所期望的结果3.9 - (3.9 % 0.1)3.9(事实上,谷歌说,我不是要疯了),但是当我在Java中,我得到运行它3.8000000000000003

I understand this is a result of how Java stores and processes doubles, but is there a way to work around it?

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

回答by Aaron Digulla

Use a precise type if you need a precise result:

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

    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

Why 3.8000...003? Because Java uses the FPU to calculate the result. 3.9 is impossible to store exactly in IEEE double precision notation, so it stores 3.89999... instead. And 3.8999%0.01 gives 0.09999... hence the result is a little bit bigger than 3.8.

为什么是 3.8000...003?因为Java使用FPU来计算结果。3.9 不可能完全以 IEEE 双精度表示法存储,因此它存储 3.89999... 代替。3.8999%0.01 给出 0.09999...因此结果比 3.8 大一点。

回答by MAK

From The Java Language Specification:

来自Java 语言规范

The result of a floating-point remainder operation as computed by the % operator is not the same as that produced by the remainder operation defined by IEEE 754. The IEEE 754 remainder operation computes the remainder from a rounding division, not a truncating division, and so its behavior is not analogous to that of the usual integer remainder operator. Instead, the Java programming language defines % on floating-point operations to behave in a manner analogous to that of the integer remainder operator; this may be compared with the C library function fmod. The IEEE 754 remainder operation may be computed by the library routine Math.IEEEremainder.

由 % 运算符计算的浮点余数运算的结果与由 IEEE 754 定义的余数运算产生的结果不同。 IEEE 754 余数运算通过舍入除法而不是截断除法计算余数,并且所以它的行为与通常的整数余数运算符的行为不同。相反,Java 编程语言在浮点运算上定义 % 以类似于整数余数运算符的方式运行;这可以与 C 库函数 fmod 进行比较。IEEE 754 余数运算可由库例程 Math.IEEEremainder 计算。

In other words, this is due to the fact that Java rounds the result of the division involved in computing the remainder, while IEEE754 specifies truncating the answer of the division. This particular case seems to expose this difference very clearly.

换句话说,这是由于Java对计算余数所涉及的除法结果进行四舍五入,而IEEE754规定截断除法的结果。这个特殊的案例似乎非常清楚地暴露了这种差异。

You can get the answer you expect using Math.IEEEremainder:

您可以使用 Math.IEEEremainder 得到您期望的答案:

System.out.println(3.9 - (3.9 % 0.1));
System.out.println(3.9 - Math.IEEEremainder(3.9, 0.1));

回答by Sjoerd

You could use java.math.BigDecimal and its method divideAndRemainder().

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

回答by Wolph

If you know the amount of decimals you're dealing with, you could try to convert to integers first. This is just a classic case of floating point inacuraccy. Instead of doing 3.9 % 0.1you're doing something like 3.899 % 0.0999

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