Java模运算符导致错误的结果?

时间:2011-08-16 19:07:30

标签: java floating-point floating-accuracy modulo

我怀疑之前已经问过,但似乎找不到匹配的问题......

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

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

这些行的结果是

5.0 
1.7763568394002505E-15 

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

有解决方法吗?

谢谢!

4 个答案:

答案 0 :(得分:12)

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
    }
}

答案 1 :(得分:2)

算术

首先,十进制数5.6不能用二进制浮点精确表示。它四舍五入到精确的二进制分数3152519739159347/2 49

28.0 / 5.6 = 5.0的事实是因为5.0是与真实结果最接近的double数字,其中真实结果为5.0000000000000003172065784643 ....

对于28.0%5.6,真实结果恰好是1/2 49 ,大约是1.776×10 -15 ,因此计算正确舍入。 / p>

变通方法

为什么需要解决方法?对于大多数应用程序,保持稍微错误的结果是好的。您是否担心显示“漂亮”的结果?

如果您需要绝对精确的算术,那么您将需要使用BigFraction的一些实现。

进一步阅读

浮点警告的主题已在各种文章中介绍过:

(以读者友好的顺序递减。)

答案 2 :(得分:2)

结果实际上不是0,但它非常接近(0.00000000000000177635 ......)。问题是一些十进制数字不能用二进制表示,所以这就是问题的来源;我怀疑在C / C ++中会打印出相同的结果。

答案 3 :(得分:0)

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

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));
    }
}