Java 模运算符结果不好?
我怀疑这个问题以前被问过,但似乎找不到匹配的问题...
我正在使用 Scala,但我很确定这只是一个 Java 问题...输入值是双精度
println(28.0 / 5.6)
println(28.0 % 5.6)
的结果这意味着
5.0
1.7763568394002505E-15
Java 正在正确执行除法,但由于某种原因导致模数错误,因为对于任何解析为整数的除法问题,模数应该为 0...
有解决方法吗?
谢谢!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
5.0 只是表明 Java 理解的精确结果比任何其他双精度值更接近 5.0。这并不意味着运算的精确结果恰好为 5。
现在,当您询问模数时,您可以得到更精细的细节,因为结果不是固定为“5”部分。
这不是一个很好的解释,但想象一下您有一个具有 4 位精度的十进制浮点类型。 1000 / 99.99 和 1000 % 99.99 的结果是什么?
嗯,真正的结果从 10.001001 开始 - 所以你必须将其四舍五入到 10.00。然而,余数是 0.10,您可以表达它。再说一次,除法看起来好像给你一个整数,但它不完全。
考虑到这一点,请记住,您的 5.6 的文字实际上是 5.5999999999999996447286321199499070644378662109375。现在显然 28.0(*可以)精确地除以该数字并不完全是 5。
编辑:现在,如果您使用 BigDecimal 执行 十进制 浮点算术结果,该值实际上正好是 5.6,并且没有问题:
The 5.0 just shows that the precise result as Java understands it is closer to 5.0 than it is to any other double. That doesn't mean the precise result of the operation is exactly 5.
Now when you ask for the modulus, you're able to down to a much finer level of detail, because the result isn't pinned to having the "5" part.
That's not a great explanation, but imagine you had a decimal floating point type with 4 digits of precision. What's the result of 1000 / 99.99 and 1000 % 99.99?
Well, the real result starts with 10.001001 - so you have to round that to 10.00. However, the remainder is 0.10, which you can express. So again, it looks like the division gives you a whole number, but it doesn't quite.
With that in mind, bear in mind that your literal of 5.6 is actually 5.5999999999999996447286321199499070644378662109375. Now clearly 28.0 (which *can) be represented exactly divided by that number isn't exactly 5.
EDIT: Now if you perform the result with decimal floating point arithmetic using
BigDecimal
, the value really is exactly 5.6, and there are no problems:结果实际上不是 0,但非常接近( 0.00000000000000177635... )。问题是一些十进制数无法用二进制精确表示,所以这就是问题所在;我怀疑用 C/C++ 会打印出相同的结果。
The result isn't actually 0, but it's pretty close( 0.00000000000000177635... ). The problem is that some decimal numbers can't be represented exactly in binary, so that's where the issue is coming in; I'd suspect that the same result would be printed out in C/C++.
算术
首先,十进制数 5.6 无法用二进制浮点数精确表示。它四舍五入为精确的二进制小数 3152519739159347/249。
28.0 / 5.6 = 5.0 是因为 5.0 是最接近真实结果的 double 数,其中真实结果是 5.0000000000000003172065784643....
至于 28.0 % 5.6,真实结果恰好是 1 /249,大约是1.776 × 10−15,因此计算结果正确舍入。
解决方法
为什么需要解决方法?对于大多数应用程序来说,保留稍微错误的结果就可以了。您是否担心显示“漂亮”的结果?
如果您需要绝对精确的算术,那么您将需要使用 BigFraction 的某些实现。
进一步阅读
各种文章中都介绍了浮点警告主题:
(按读者友好程度降序排列。)
Arithmetic
First of all, the decimal number 5.6 cannot be represented exactly in binary floating-point. It is rounded to the exact binary fraction 3152519739159347/249.
The fact that 28.0 / 5.6 = 5.0 is because 5.0 is the closest
double
number to the true result, where the true result is 5.0000000000000003172065784643....As for 28.0 % 5.6, the true result is exactly 1/249, which is approximately 1.776 × 10−15, so the calculation is correctly rounded.
Workarounds
Why do you need a workaround? For most applications, keeping the very slightly wrong result is fine. Are you concerned about displaying "pretty" results?
If you need absolutely precise arithmetic, then you will need to use some implementation of BigFraction.
Further reading
The topic of floating-point caveats has been covered in a variety of articles:
(In decreasing order of reader-friendliness.)
这是我使用模运算符检查 double 值是否可以被另一个值整除的解决方案:
This is my solution for check if double value is divisible by another using modulo operator: