【问题标题】:Comparing float and double primitives in Java比较 Java 中的 float 和 double 原语
【发布时间】:2011-11-15 13:41:00
【问题描述】:

我遇到了 Java 的一个奇怪的角落。(我觉得很奇怪)

double dd = 3.5;          
float ff = 3.5f;
System.out.println(dd==ff);   

o/p: 真

double dd = 3.2;
float ff = 3.2f;
System.out.println(dd==ff);

o/p: 假

我观察到,如果我们将任何两个值(我在示例中提到的浮点数和双精度数)与 .5.0 进行比较,例如 3.5、234.5、645.0 然后输出为true,即两个值相等,否则输出为false,尽管它们相等。

即使我尝试创建方法strictfp,但没有运气。 我错过了什么吗?

【问题讨论】:

  • ...我们又来了。
  • @Kerrek:你能带我一起去吗?
  • @Ajinka:大约每三天出现一次关于浮点类型的问题,对于值的精确表示具有完全相同的混淆。 :-)
  • @Ajinkya:确实——就像带着炸弹旅行来利用统计数据为你带来优势:-)

标签: java operators primitive


【解决方案1】:

看看What every computer scientist should know about floating point numbers

将无限多个实数压缩成有限位数需要一个近似表示......

--- 编辑以显示上述引用的含义---

您永远不应该比较浮点数或双精度数是否相等;因为,您不能真正保证分配给 float 或 double 的数字是准确的。

所以

 float x = 3.2f;

不会产生值为 3.2 的浮点数。它导致一个值为 3.2 的浮点数加上或减去一些非常小的错误。说 3.19999999997f。现在应该很明显为什么比较不起作用了。

要合理比较浮点数是否相等,您需要检查该值是否“足够接近”相同的值,就像这样

float error = 0.000001 * second;
if ((first >= second - error) || (first <= second + error)) {
   // close enough that we'll consider the two equal
   ...
}

【讨论】:

  • :感谢您的链接。你能告诉我我缺少什么吗?
  • @Ajinkya,提供解释。祝你的项目好运。
  • 这是float comparison 上的一篇非常有趣的文章——一般来说,使用浮点数的 epsilon 和相对错误检查是好的,但利用 IEEE754 格式的性质更好:-)跨度>
  • @Kerrek:感谢您提供的链接,我会试试看 :)
  • 最后一段代码不适用于负值。将 error 包裹在 Math.abs() 中以使其正常工作。
【解决方案2】:

浮点数的常见实现,IEEE754,只允许精确表示那些具有短的、有限的二进制展开的数字,即它们是有限多(附近)幂的和两个。所有其他数字都无法精确表示。

由于floatdouble 具有不同的大小,因此不可表示的值在两种类型中的表示不同,因此它们比较不相等。

(二进制字符串的长度是尾数的大小,所以float是24,double是53,80位扩展精度浮点数是64(不是在 Java 中)。比例由指数确定。)

【讨论】:

  • 感谢您带我一起去 :)
  • 没问题 - 这是一种心理复制/粘贴,所以没有伤害:-) 如果您打算比较浮动,请查看浮动比较文章。这真的很有启发性。
【解决方案3】:

不同之处在于,3.5 可以在floatdouble准确地表示 - 而 3.2 不能在任何一种类型中准确地表示...并且两个最接近的近似值是不同的.

假设我们有两种固定精度的十进制类型,其中一种存储 4 个有效数字,另一种存储 8 个有效数字,我们要求它们中的每一个存储最接近“a第三”(但我们可能会这样做)。那么一个的值为 0.3333,一个值为 0.33333333。

floatdouble 之间的相等比较首先将 float 转换为 double,然后比较两者 - 这相当于将我们的“小十进制”类型中的 0.3333 转换为 0.33330000。然后它将比较 0.33330000 和 0.33333333 是否相等,并给出错误的结果。

【讨论】:

  • 非常感谢您回答我多余的问题。也感谢你让它变得非常简单:)
  • 感谢您的简单解释
【解决方案4】:

浮点是一种二进制格式,它可以将数字表示为 2 的幂的和。例如3.5 是 2 + 1 + 1/2。

浮动 3.2f 作为 3.2 的近似值

2 + 1 + 1/8+ 1/16+ 1/128+ 1/256+ 1/2048+ 1/4096+ 1/32768+ 1/65536+ 1/524288+ 1/1048576+ 1/4194304 + a small error

但是 3.2d 的两倍是 3.2 的近似值

2 + 1 + 1/8+ 1/16+ 1/128+ 1/256+ 1/2048+ 1/4096+ 1/32768+ 1/65536+ 1/524288+ 1/1048576+ 1/8388608+ 1/16777216+ 1/134217728+ 1/268435456+ 1/2147483648+ 1/4294967296+ 1/34359738368+ 1/68719476736+ 1/549755813888+ 1/1099511627776+ 1/8796093022208+ 1/17592186044416+ 1/140737488355328+ 1/281474976710656+ 1/1125899906842624 + a smaller error

使用浮点数时,需要使用适当的舍入。如果您改用 BigDecimal(很多人都这样做),它会内置四舍五入。

double dd = 3.2;          
float ff = 3.2f;
// compare the difference with the accuracy of float.
System.out.println(Math.abs(dd - ff) < 1e-7 * Math.abs(ff));

顺便说一句,我用来打印双精度分数的代码。

double f = 3.2d;
double f2 = f - 3;
System.out.print("2+ 1");
for (long i = 2; i < 1L << 54; i <<= 1) {
  f2 *= 2;
  if (f2 >= 1) {
    System.out.print("+ 1/" + i);
    f2 -= 1;
  }
}
System.out.println();

【讨论】:

  • 虽然你的答案看起来很复杂,但它确实有助于理解隐藏的东西。
【解决方案5】:

这应该可行:

BigDecimal ddBD = new BigDecimal(""+dd);
BigDecimal ffBD = new BigDecimal(""+ff);

// test for equality
ddBO.equals(ffBD);

当您想要比较浮点数或双精度时,请始终使用 BigDecimal 并始终使用带有 String 参数的 BigDecimal 构造函数!

【讨论】:

    猜你喜欢
    • 2018-11-30
    • 1970-01-01
    • 1970-01-01
    • 2011-10-10
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    • 2012-04-19
    • 1970-01-01
    相关资源
    最近更新 更多