为什么比较Java中的浮点数不一致?

时间:2013-05-18 18:52:36

标签: java floating-point

class Test{  
    public static void main(String[] args){  
        float f1=3.2f;  
        float f2=6.5f;  

        if(f1==3.2){
            System.out.println("same");  
        }else{
            System.out.println("different");  
        }
        if(f2==6.5){
            System.out.println("same");  
        }else{  
            System.out.println("different");  
        }
    }  
}  

输出:

different
same

为什么输出会那样?我在第一种情况下期望same作为结果。

8 个答案:

答案 0 :(得分:40)

区别在于6.5可以精确地表示float和double,而3.2不能精确地表示在任何一种类型中。两个最接近的近似值是不同的。

float和double之间的相等比较首先将float转换为double,然后比较两者。所以数据丢失了。


你不应该比较浮动或双打的平等;因为你不能真正保证你指定给float或double的数字是准确的。

此舍入误差是浮点计算的一个特征

  

将无数多个实数压缩成有限数量的位   需要近似的表示。虽然有无限的   许多整数,在大多数程序中,整数计算的结果都可以   以32位存储。

     

相反,给定任意数量的位,   大多数带有实数的计算都会产生数量   不能使用那么多位来精确表示。因此   浮点计算的结果通常必须按顺序舍入   以适应其有限的表示。这个舍入误差是   浮点计算的特征。

检查What Every Computer Scientist Should Know About Floating-Point Arithmetic for more!

答案 1 :(得分:25)

它们都是the IEEE floating point standard不同部分的实现。 float宽度为4个字节,而double宽度为8个字节。

根据经验,在大多数情况下,您可能更愿意使用double,而只有在有充分理由时才使用float。 (使用float而不是double的一个很好的理由示例是“我知道我不需要那么精确,我需要存储一百万它们在记忆中。“)还值得一提的是,很难证明你不需要double精度。

此外,在比较浮点值是否相等时,您通常希望使用类似Math.abs(a-b) < EPSILON的内容,其中ab是要比较的浮点值,{{1 }}是一个小的浮点值,如EPSILON。这样做的原因是浮点值很少编码它们“应该”的确切值 - 相反,它们通常编码的值非常接近 - 所以当你确定两个值是否相同时你必须“斜视”。

编辑:每个人都应该阅读下面发布的@Kugathasan Abimaran链接:What Every Computer Scientist Should Know About Floating-Point Arithmetic for more!

答案 2 :(得分:5)

要查看您要处理的内容,可以使用FloatDouble的toHexString方法:

class Test {
    public static void main(String[] args) {
        System.out.println("3.2F is: "+Float.toHexString(3.2F));
        System.out.println("3.2  is: "+Double.toHexString(3.2));
        System.out.println("6.5F is: "+Float.toHexString(6.5F));
        System.out.println("6.5  is: "+Double.toHexString(6.5));
    }
}
$ java Test
3.2F is: 0x1.99999ap1
3.2  is: 0x1.999999999999ap1
6.5F is: 0x1.ap2
6.5  is: 0x1.ap2

通常,如果数字等于A * 2 ^ B,则数字具有精确表示,其中A和B是整数,其允许值由语言规范设置(并且double具有更多允许值)。

在这种情况下,
6.5 = 13/2 =(1 + 10/16)* 4 =(1 + a / 16)* 2 ^ 2 == 0x1.ap2,而
3.2 = 16/5 =(1 + 9/16 + 9/16 ^ 2 + 9/16 ^ 3 + ...)* 2 ^ 1 == 0x1.999。 。 。 P1。
但Java只能保存有限数量的数字,所以它会削减.999。 。 。在某个时候关闭。 (你可能记得从数学上看0.999 ... = 1.这是在基数10中。在基数16中,它将是0.fff ... = 1。)

答案 3 :(得分:3)

class Test {  
  public static void main(String[] args) {  
    float f1=3.2f;  
    float f2=6.5f;  

    if(f1==3.2f)  
      System.out.println("same");  
    else  
      System.out.println("different");  

    if(f2==6.5f)  
      System.out.println("same");  
    else  
      System.out.println("different");  
    }  
  }

尝试这样,它会起作用。如果没有'f',您将浮动与其他浮动类型和不同精度进行比较,这可能会导致意外结果,如您的情况。

答案 4 :(得分:2)

无法直接比较floatdouble类型的值。在比较值之前,有必要将double转换为float,或将float转换为double。如果进行前一次比较,则转换会询问“float是否保持float代表double的最佳值?”如果进行后一种转换,问题将是“float是否保持double值的完美表示”。在许多情况下,前一个问题更有意义,但Java假设floatdouble之间的所有比较都是为了提出后一个问题。

我建议不管语言是否愿意容忍,一个人的编码标准绝对应该绝对禁止在floatdouble类型的操作数之间进行直接比较。给出如下代码:

float f = function1();
double d = function2();
...
if (d==f) ...

如果d表示float中无法精确表示的值,则无法确定预期的行为。如果意图是将f转换为double,并将该转化的结果与d进行比较,则应将比较写为

if (d==(double)f) ...

虽然类型转换不会改变代码的行为,但它清楚地表明代码的行为是有意的。如果意图是比较表明f是否包含float的最佳d表示,则应该是:

if ((float)d==f)

请注意,这与没有强制转换时的行为有很大不同。如果您的原始代码将每个比较的double操作数强制转换为float,则两个相等测试都会通过。

答案 5 :(得分:1)

由于近似问题,通常使用带浮点数的==运算符不是一个好习惯。

答案 6 :(得分:0)

6.5可以用二进制表示,而3.2则不能。这就是为什么6.5的精度差异无关紧要,所以6.5 == 6.5f

快速刷新二进制数的工作方式:

100 -> 4

10 -> 2

1 -> 1

0.1 -> 0.5 (or 1/2)

0.01 -> 0.25 (or 1/4)

6.5二进制文件:110.1(确切的结果,其余数字只是零)

3.2 in binary:11.001100110011001100110011001100110011001100110011001101...(这里的精度很重要!)

一个浮点只有24位精度(其余用于符号和指数),所以:

3.2f in binary:11.0011001100110011001100(不等于双精度近似值)

基本上它与你用十进制数写1/5和1/7时相同:

1/5 = 0,2
1,7 = 0,14285714285714285714285714285714.

答案 7 :(得分:-1)

Float的精度低于double,bcoz float使用32位,其中1用于Sign,23用于精度,8用于Exponent。其中double使用64位,其中52位用于精度,11位用于指数,1位用于Sign ....精度是重要的事项。表示为float和double的十进制数可以相等或不相等取决于是否需要精度(即范围)小数点后的数字可以变化)。关心S. ZAKIR