浮点数和双精度数的混淆

9
我对浮点数的行为感到困惑,因为我看到了下面代码片段的结果。
    float var1 = 5.4f;
    float var2 = 5.5f;

    if(var1 == 5.4)
        System.out.println("Matched");
    else
        System.out.println("Oops!!");

    if(var2 == 5.5)
        System.out.println("Matched");
    else
        System.out.println("Oops!!");

输出:

Oops!!
Matched

这是因为十进制数在二进制格式中可能无法准确表示吗? 还是因为我将一个float类型的变量与一个double类型的变量进行比较时精度不够?如果是这样,为什么对于下一个变量它可以正常工作呢?


有很多信息,只需在谷歌上搜索 :) 首先,使用双精度而不是单精度,还可以查看 BigDecimal... - vikingsteve
另一个问题在这个链接中很好地讨论了这个问题:https://dev59.com/y3NA5IYBdhLWcg3wHp6B - AurA
5个回答

16
这是因为十进制数在基于二的二进制格式中无法精确表示。基本上,5.4f5.4d都不是5.4的精确表示,因此它们不同。而5.5f5.5d是相同的,因为它们都是5.5的精确表示。
需要注意的是,5.4隐式地与5.4d相同 - 浮点字面量的默认类型是double。任何使用floatdouble操作数的二进制运算符将将float提升为double,并执行两个double值的运算。
十进制类型的角度来看可能会更容易理解。假设我们有两种类型Decimal5和Decimal10,它们分别是具有5或10个有效数字的十进制数。然后考虑 "三分之一" 和 "四分之一":
A third:
Decimal5:  0.33333
Decimal10: 0.3333333333

A quarter (showing trailing zeroes just for clarity):
Decimal5:  0.25000
Decimal10: 0.2500000000

当比较最接近三分之一的Decimal5值和Decimal10值时,Decimal5值会被转换为0.3333300000的Decimal10值,这与0.3333333333不相等。这与您的第一个示例类似。

然而,当比较四分之一的值时,0.25000的Decimal5值被转换为0.2500000000,这与我们对四分之一的Decimal10值相同。这与您的第二个示例类似。

当然,二进制浮点类型比这要复杂一些,其中包括规范化、次规范数等-但是对于您的示例,这种类比已经足够接近了。


实际上,还有一个没有讨论的问题。当你执行if (var1 == 5.4)时,到底发生了什么?看起来var1被隐式转换为double类型并与5.4d进行比较。 - thang
@thang:是的 - 我在我的比喻中有点讨论了这个问题,展示了将Decimal5值转换为Decimal10值的过程。 - Jon Skeet
有一件特定的事情我想要说的是,在 if (var1 == 5.4) 中会发生什么?这与 Java 如何处理 5.4 有关。为什么它不会隐式地将 5.4 转换为浮点数?你的类比只是说明了 5.4f 和 5.4d 不同,但为什么 5.4f 和 5.4d 不同会导致 if 语句为 false 呢? - thang
@thang:因为它是没有 f 的字面量,这意味着它隐式地是一个 double。我已经在答案中进行了编辑。 - Jon Skeet
是的,我知道。我想让你把它放到答案中,这样下一个读者就能理解了。不过很酷。我还要补充一点,它会自动将浮点变量var1提升为双精度。 - thang
@thang: 对的,我加上了关于推广的部分。 - Jon Skeet

6

3

用以下代码替换您的if条件:

if(var1 == 5.4f)
    System.out.println("Matched");
else
    System.out.println("Oops!!");

if(var2 == 5.5f)
    System.out.println("Matched");
else
    System.out.println("Oops!!");

然后它将两次打印“匹配”。原因是因为在末尾没有限定符“f”的情况下,Java将5.4视为double类型,与5.5相比无法准确地表示。请保留HTML标记。

从问题中可以清楚地看出提问者知道在添加一个尾随的 "f" 和不添加之间的区别。他的问题是为什么其中一个浮点数与其双精度计数器相等,而另一个则不相等。@Jon Skeet 已经正确回答了这个问题。 - Andromeda

2

很容易看出:

if(var1 == 5.4f)
    System.out.println("Matched");
else
    System.out.println("Oops!!");

if(var2 == 5.5f)
    System.out.println("Matched");
else
    System.out.println("Oops!!");

输出:

Matched
Matched

5.4f5.4d 的二进制表示不完全相同。


过于追求细节:5.5f和5.5d的二进制表示也不相同(如果您直接比较内存)。您可能想表达的是,5.4f和5.4d的二进制表示所代表的实际数字并不相同。 - thang

0

如果您想比较浮点类型,必须使用epsilon进行比较。在这里,您可以看到所有可用于比较浮点类型的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接