在Java中比较浮点数和双精度浮点数

5
以下代码使用了类型为floatdouble的值。
float a=99999.99F;
double b=a;

System.out.println("a : "+a);
System.out.println("b : "+b);
System.out.println("a==b : "+(a==b));

它显示以下输出:
a : 99999.99
b : 99999.9921875
a==b : true

执行后,ab的值分别为99999.9999999.9921875,尽管a==b返回true
比较表达式a==b为什么会返回true

4
在任何语言中,浮点数不能直接进行比较,必须通过与范围 > 和 < epsilon 进行比较才能更精确。请注意不要更改原意。 - amar
这个答案也值得一看:[链接](https://dev59.com/h2865IYBdhLWcg3wnP2a#3730249) - KrishPrabakar
1
@amar:这不是问题所在,也不是普遍适用的建议。这是一个错误的神话,源于对浮点运算工作原理的不理解。 - Eric Postpischil
4
这个问题并不是那个问题的重复,因为在这个问题中,被比较的数字完全相等,而 == 运算符返回了正确的结果。 - Eric Postpischil
1
参见:https://dev59.com/ZnVC5IYBdhLWcg3wixw0 - Pascal Cuoq
4个回答

7
在最后一行中,你比较a==b时,将a的值隐式地提升为double类型进行比较。因此,实际上正在评估的是(((double)a)==b),当然,这将评估为true,因为首先将a作为double类型初始化。

换句话说,最后一个布尔表达式(a==b)是在询问:

是否(将某个浮点值转换为double类型)==(在其他地方将同一浮点值转换为double类型)

答案是:是。

更新:下面的评论者指出了一个很好的观点:当将a升级为double类型时,存储的数字值实际上并没有改变,尽管它看起来是因为打印了不同的值。问题在于浮点数存储的方式;如果不让这个答案变得太长并且涉及到我的知识深度以外的领域,你需要知道的是,一些简单的十进制值不能被二进制浮点形式完美表示,无论你有多少个数字位数(就像你不能使用有限数量的3来完美表示10进制的1/3一样,如0.333333...),因此,当你使用float(浮点数的32位表示)分配其中一个这些值,然后将其转换为double(64位表示),并使用println显示该数字的值时,你可能会看到更多的数字。

println方法将显示可转换为计算机内部看到的相同float(或double)值的最短十进制数字字符串。虽然在您的示例中前两个调用println中传递了相同的数值,但是double类型具有更高的精度,因此println需要打印更多的数字以显示“将转换为(double)99999.99F的最短十进制数字字符串”。如果您的示例为9999.50,则两个数字将打印相同的结果,因为二进制数可以很好地表示1/2

注意:我离浮点数专家还差得远呢;如果您想深入了解此处发生的情况,请查看(http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html )。

1
你的解释很好,但我希望你的答案能够说明将float值转换为double不会改变其值。每个float值都可以表示为double,在转换时,选择的double是表示相同值的double。让OP感到惊讶的行为出现在Java的打印函数中(但不是在转换为double==中)。 - Pascal Cuoq
@PascalCuoq 谢谢您的评论 - 我更新了我的答案以解决这个问题(希望足够充分):^) - JVMATL
我稍微编辑了一下你的解释,希望你不介意。 - Pascal Cuoq
我不太擅长的领域是Java。在这个问题中,“显示转换为相同浮点数或双精度浮点数的最短十进制数字字符串”实际上是应用于“字符串”和“浮点数”(以及分别应用于“字符串”和“双精度浮点数”)时“+”的行为,不是吗? - Pascal Cuoq
@PascalCuoq 是的 - 在Java中,当你有a_string + <not_a_string>时,它会隐式地将表达式转换为(a_string + String.valueOf(not_a_string)) -- 而String.valueOf()被重载以处理所有基本类型,以及对象。因此,如果你将(连接)一个浮点数添加到一个字符串中,它不会在打印之前将浮点数提升为双精度。 - JVMATL

4
这是因为在IT技术中,常常需要处理大量的数据。
double b = a;

浮点值被转换(扩展)为双精度浮点数,但在转换过程中并不会丢失数据。
b == a

将a中的相同浮点值再次转换为双精度以与b进行比较。因此,两次转换的结果是相同的。


3
打印 ab 的原因是:
System.out.println("a : "+a);
System.out.println("b : "+b);

由于Java默认的浮点数打印仅显示足以区分其类型的数字,因此显示了不同的结果。在float a=99999.99F;中,值99999.99在float中无法准确表示,因此会转换为最接近的可表示值(99999.9921875),并将a设置为此值。
当打印a时,“99999.99”就足够显示该值了。
然后double b=a;b设置为完全相同的值。但是,由于b是双精度型,因此在double中有其他可表示的值比99999.99更接近。如果您使用了double b = 99999.99;,则double具有更高的精度意味着b将被设置为更接近99999.99的值(99999.990000000005238689482212066650390625)。但是,您的代码已将b设置为比那个更远离99999.99的值。因此,当打印b时,Java必须使用更多的数字来显示其值不是最接近99999.99的double
最后,当您比较ab时,比较返回true,因为ab具有完全相同的值。
总之:ab是完全相等的,但是由于在double中表示得更细致,所以打印b时会使用更多数字。

1
如果使用不同的值,可能更容易看出发生了什么。例如,如果使用值1234512.345f,则该值将打印为1234512.4,但如果转换为double,则将打印为1234512.375。最接近1234512.345的float值恰好等于1234512.375;两侧的值分别为1234512.25和1234512.5;由于值1234512.4比这两个值更接近1234512.375,因此没有必要使用更多数字来表示更精确的内容。在1234512.375和1234512.4之间有许多百万个双倍值;因此,较短的表示形式不足以显示该数量作为double。Java将使用尽可能多的数字来唯一地标识该值作为double

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