Java 模运算符结果不好?

发布于 2024-11-29 18:52:09 字数 313 浏览 1 评论 0 原文

我怀疑这个问题以前被问过,但似乎找不到匹配的问题...

我正在使用 Scala,但我很确定这只是一个 Java 问题...输入值是双精度

println(28.0 / 5.6) 
println(28.0 % 5.6)

的结果这意味着

5.0 
1.7763568394002505E-15 

Java 正在正确执行除法,但由于某种原因导致模数错误,因为对于任何解析为整数的除法问题,模数应该为 0...

有解决方法吗?

谢谢!

I suspect this has been asked before, but can't seem to find a question that matches...

I'm using Scala, but I'm pretty sure this is just a Java problem... input values are doubles

println(28.0 / 5.6) 
println(28.0 % 5.6)

The result of these lines is

5.0 
1.7763568394002505E-15 

Which means that Java is performing the division correctly, but for some reason getting the modulo wrong, since the modulo should be 0 for any division problem that resolves to a whole number...

Is there a workaround for this?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

停滞 2024-12-06 18:52:09

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,并且没有问题:

import java.math.BigDecimal;

public class Test {
    public static void main(String[] args) {
        BigDecimal x = new BigDecimal("28.0");
        BigDecimal y = new BigDecimal("5.6");

        BigDecimal div = x.divide(y);
        BigDecimal rem = x.remainder(y);

        System.out.println(div); // Prints 5
        System.out.println(rem); // Prints 0.0
    }
}

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:

import java.math.BigDecimal;

public class Test {
    public static void main(String[] args) {
        BigDecimal x = new BigDecimal("28.0");
        BigDecimal y = new BigDecimal("5.6");

        BigDecimal div = x.divide(y);
        BigDecimal rem = x.remainder(y);

        System.out.println(div); // Prints 5
        System.out.println(rem); // Prints 0.0
    }
}
新雨望断虹 2024-12-06 18:52:09

结果实际上不是 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++.

初相遇 2024-12-06 18:52:09

算术

首先,十进制数 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.)

黑寡妇 2024-12-06 18:52:09

这是我使用模运算符检查 double 值是否可以被另一个值整除的解决方案:

public class DoubleOperation
{
    public static final double EPSILON = 0.000001d;

    public static boolean equals(double val1, double val2)
    {
        return (Math.abs(val1 - val2) < EPSILON);
    }

    public static boolean divisible(double dividend, double divisor)
    {
        double divisionRemainder = dividend % divisor;
        return (equals(divisionRemainder, 0.0d) || equals(divisionRemainder, divisor));
    }
}

This is my solution for check if double value is divisible by another using modulo operator:

public class DoubleOperation
{
    public static final double EPSILON = 0.000001d;

    public static boolean equals(double val1, double val2)
    {
        return (Math.abs(val1 - val2) < EPSILON);
    }

    public static boolean divisible(double dividend, double divisor)
    {
        double divisionRemainder = dividend % divisor;
        return (equals(divisionRemainder, 0.0d) || equals(divisionRemainder, divisor));
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文