【问题标题】:Comparing floats and doubles in Java比较 Java 中的浮点数和双精度数
【发布时间】:2013-12-19 13:33:36
【问题描述】:

以下代码分别使用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

【问题讨论】:

  • 浮点数不安全,不能直接比较它们在任何语言中都必须与范围进行比较 > &
  • 这个答案也值得一看:[link] (stackoverflow.com/a/3730249/248847)
  • @amar:这不是这里的问题,也不是普遍适用的建议。这是一个神话,源于对浮点运算的工作原理缺乏了解。
  • @PhilippSander:这个问题不是那个问题的重复,因为在这个问题中,被比较的数字完全相等,== 运算符返回正确的结果。

标签: java floating-point double


【解决方案1】:

在您比较a==b 的最后一行,a 的值在比较之前被隐式提升为加倍。因此,实际上,真正被评估的是(((double)a)==b),当然,它的评估结果为真,因为b 是通过将a 初始转换为double 来初始化的。

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

是(某些浮点值转换为双精度)==(相同的浮点值在其他地方转换为双精度)

答案是:是的。

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

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

注意:我什至不是浮点数专家;如果您想更深入地了解这里发生的事情,您应该查看 (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)。

【讨论】:

  • 您的解释很好,但我希望您的回答说将float 值转换为double 不会改变值。每个float 值都可以表示为一个double,并且在转换时,选择的double 是代表相同值的double。令 OP 感到惊讶的行为是在 Java 的打印功能中(但不是在转换为 double 而不是在 == 中)。
  • @PascalCuoq 感谢您的评论 - 我更新了我的答案以解决这一点(希望充分):^)
  • 我已经稍微修改了你的解释,希望你不会介意。
  • 在我的深度之外的地方是Java。在问题中,“显示将转换为相同浮点数或双精度数的最短十进制数字字符串”的行为实际上是 + 应用于 stringfloat 时的行为(分别是 string 和 @ 987654349@),不是吗?
  • @PascalCuoq 是 - 在 Java 中,当你有 a_string + 时,它会隐式地将表达式转换为 (a_string + String.valueOf(not_a_string)) -- 并且 String.valueOf() 也被重载以处理所有原始类型作为对象。因此,如果您将浮点数添加(连接)到字符串,它不会在打印之前将浮点数提升为双精度值。
【解决方案2】:

因为在

double b = a;

浮点值被转换(加宽)为双精度,但在

b == a

a 中的相同 float 值再次转换为 double 以与 b 进行比较。因此,2 次转换的结果是相同的。

【讨论】:

    【解决方案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 完全相等,但 b 打印的位数更多,因为它在 double 中表示得更精细。

    【讨论】:

      【解决方案4】:

      如果使用不同的值,可能会更容易看到发生了什么。例如,如果使用值 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 值;因此,较短的表示不足以将该数量显示为double。因此,Java 将使用尽可能多的数字来唯一地将值标识为double

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-25
        • 1970-01-01
        • 2014-04-25
        • 2015-10-22
        • 1970-01-01
        • 2018-02-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多