让:
double d = 0.1;
float f = 0.1;
应该表达
(f > d)
返回true
或false
?
根据经验,答案是true
。但是,我预计它会是false
。
As0.1
不能完美地用二进制表示,而 double 必须精确15
到16
十进制数字,而 float 只有7
. 所以,它们都小于0.1
,而双精度更接近0.1
。
我需要一个准确的解释true
。
让:
double d = 0.1;
float f = 0.1;
应该表达
(f > d)
返回true
或false
?
根据经验,答案是true
。但是,我预计它会是false
。
As0.1
不能完美地用二进制表示,而 double 必须精确15
到16
十进制数字,而 float 只有7
. 所以,它们都小于0.1
,而双精度更接近0.1
。
我需要一个准确的解释true
。
我会说答案取决于将 转换为 时的舍入double
模式float
。float
有 24位二进制精度,double
有 53 位。在二进制中,0.1 是:
0.1₁₀ = 0.0001100110011001100110011001100110011001100110011…₂
^ ^ ^ ^
1 10 20 24
所以如果我们在第 24 位四舍五入,我们会得到
0.1₁₀ ~ 0.000110011001100110011001101
它大于精确值和更精确的 53 位近似值。
数字 0.1 将四舍五入为具有给定精度的最接近的浮点表示。该近似值可能大于或小于 0.1,因此如果不查看实际值,您无法预测单精度或双精度近似值是否更大。
这是双精度值四舍五入的结果(使用 Python 解释器):
>>> "%.55f" % 0.1
'0.1000000000000000055511151231257827021181583404541015625'
这是单精度值:
>>> "%.55f" % numpy.float32("0.1")
'0.1000000014901161193847656250000000000000000000000000000'
所以你可以看到单精度近似值更大。
如果你转换.1
成二进制,你会得到:
0.000110011001100110011001100110011001100110011001100...
永远重复
映射到数据类型,你得到:
浮点数(.1) = %.00011001100110011001101 ^--- 注意四舍五入 双(.1) = %.0001100110011001100110011001100110011001100110011010
将其转换为以 10 为底:
浮动(.1)= .10000002384185791015625 双(.1)= .100000000000000088817841970012523233890533447265625
这是从布鲁斯道森写的一篇文章中摘录的。可以在这里找到:
双打不是浮点数,所以不要比较它们
我认为Eric Lippert对这个问题的评论实际上是最清楚的解释,所以我将其重新发布为答案:
假设您正在计算 3 位小数和 6 位小数的 1/9。0.111 < 0.111111,对吗?
现在假设您正在计算 6/9。0.667 > 0.666667,对吗?
您不能认为三位小数中的 6/9 是 0.666,因为这不是最接近 6/9 的三位小数!
由于无法准确表示,所以比较以 2 为底的 1/10 就像以 10 为底比较 1/7。
1/7 = 0.142857142857...但是在不同的以 10 为底的精度(3 位与 6 位小数)进行比较,我们有 0.143 > 0.142857。
只是添加到其他关于 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
?了解更多信息。
在转换中,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;
}