100

让:

double d = 0.1;
float f = 0.1;

应该表达

(f > d)

返回truefalse

根据经验,答案是true。但是,我预计它会是false

As0.1不能完美地用二进制表示,而 double 必须精确1516十进制数字,而 float 只有7. 所以,它们都小于0.1,而双精度更接近0.1

我需要一个准确的解释true

4

7 回答 7

172

我会说答案取决于将 转换为 时的舍入double模式floatfloat有 24位二进制精度,double有 53 位。在二进制中,0.1 是:

0.1₁₀ = 0.0001100110011001100110011001100110011001100110011…₂
             ^        ^         ^   ^ 
             1       10        20  24

所以如果我们在第 24 位四舍五入我们会得到

0.1₁₀ ~ 0.000110011001100110011001101

它大于精确值和更精确的 53 位近似值。

于 2013-10-10T09:46:56.730 回答
55

数字 0.1 将四舍五入为具有给定精度的最接近的浮点表示。该近似值可能大于或小于 0.1,因此如果不查看实际值,您无法预测单精度或双精度近似值是否更大。

这是双精度值四舍五入的结果(使用 Python 解释器):

>>> "%.55f" % 0.1
'0.1000000000000000055511151231257827021181583404541015625'

这是单精度值:

>>> "%.55f" % numpy.float32("0.1")
'0.1000000014901161193847656250000000000000000000000000000'

所以你可以看到单精度近似值更大。

于 2013-10-10T09:43:39.867 回答
19

如果你转换.1成二进制,你会得到:

0.000110011001100110011001100110011001100110011001100...

永远重复

映射到数据类型,你得到:

浮点数(.1) = %.00011001100110011001101
                                     ^--- 注意四舍五入
双(.1) = %.0001100110011001100110011001100110011001100110011010

将其转换为以 10 为底:

浮动(.1)= .10000002384185791015625
双(.1)= .100000000000000088817841970012523233890533447265625

这是从布鲁斯道森写的一篇文章中摘录的。可以在这里找到:
双打不是浮点数,所以不要比较它们

于 2013-10-10T14:13:55.000 回答
5

我认为Eric Lippert对这个问题的评论实际上是最清楚的解释,所以我将其重新发布为答案:

假设您正在计算 3 位小数和 6 位小数的 1/9。0.111 < 0.111111,对吗?

现在假设您正在计算 6/9。0.667 > 0.666667,对吗?

您不能认为三位小数中的 6/9 是 0.666,因为这不是最接近 6/9 的三位小数!

于 2013-10-14T12:49:15.520 回答
4

由于无法准确表示,所以比较以 2 为底的 1/10 就像以 10 为底比较 1/7。

1/7 = 0.142857142857...但是在不同的以 10 为底的精度(3 位与 6 位小数)进行比较,我们有 0.143 > 0.142857。

于 2013-10-10T17:51:12.510 回答
1

只是添加到其他关于 IEEE-754 和 x86 的答案:这个问题比看起来更复杂。IEEE-754 中没有 0.1 的“一个”表示 - 有两个。向下或向上舍入最后一位数字都是有效的。这种差异可以而且确实会发生,因为 x86 不使用64 位进行其内部浮点计算;它实际上使用 80 位!这称为双扩展精度

因此,即使只是在 x86 编译器中,有时也会发生相同的数字以两种不同的方式表示,因为有些用 64 位计算其二进制表示,而另一些则使用 80。


事实上,即使使用相同的编译器,甚至在同一台机器上,它也可能发生!

#include <iostream>
#include <cmath>

void foo(double x, double y)
{
  if (std::cos(x) != std::cos(y)) {
    std::cout << "Huh?!?\n";  //← you might end up here when x == y!!
  }
}

int main()
{
  foo(1.0, 1.0);
  return 0;
}

请参阅为什么cos(x) != cos(y)即使x == y了解更多信息。

于 2013-10-10T16:44:04.400 回答
1

在转换中,double 的等级大于 float 的等级。通过进行逻辑比较, f 被强制转换为 double 并且您正在使用的实现可能会给出不一致的结果。如果您为 f 加上后缀,编译器将其注册为浮点数,那么您将得到 0.00,这在 double 类型中为 false。无后缀的浮点类型是双精度的。

#include <stdio.h>
#include <float.h>

int main()
{
     double d = 0.1;
     float f = 0.1f;
     printf("%f\n", (f > d));

     return 0;
}
于 2013-10-11T14:14:17.840 回答