设:
double d = 0.1;
float f = 0.1;
是否应该表达
(f > d)
是true
还是false
?
根据实证,答案是true
。然而,我预期它应该是false
。
由于0.1
无法在二进制中完全表示,而双精度浮点数的精度为15
到16
位小数,单精度浮点数只有7
位小数。因此,它们两者都比0.1
要小,而双精度浮点数更接近0.1
。
我需要一个关于true
的确切解释。
设:
double d = 0.1;
float f = 0.1;
是否应该表达
(f > d)
是true
还是false
?
根据实证,答案是true
。然而,我预期它应该是false
。
由于0.1
无法在二进制中完全表示,而双精度浮点数的精度为15
到16
位小数,单精度浮点数只有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...永远重复
float(.1) = %.00011001100110011001101 ^--- 注意四舍五入 double(.1) = %.0001100110011001100110011001100110011001100110011010将其转换为十进制:
float(.1) = .10000002384185791015625 double(.1) = .100000000000000088817841970012523233890533447265625这是Bruce Dawson撰写的一篇文章中的内容。可以在此处找到:
由于无法精确表示,将十进制中的1/10转换成二进制就像将十进制中的1/7进行比较一样。
1/7 = 0.142857142857... 但在不同的十进制精度下进行比较(3位小数与6位小数),我们有0.143 > 0.142857。
我认为Eric Lippert的评论对这个问题的解释是最清晰的,所以我会将其作为答案重新发布:
假设你正在使用三位小数和六位小数计算1/9。 0.111 < 0.111111,对吧?
现在假设你正在计算6/9。 0.667 > 0.666667,对吧?
你不能说6/9的三位小数是0.666,因为那不是最接近6/9的三位小数!
#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
?以获取更多信息。
cos(1.0)
是cos(1.0)
。这可能不是您在FLT_EVAL_METHOD = 0中得到的结果,但始终相同。 - Pascal Cuoq在转换中,double的等级高于float。通过进行逻辑比较,将f强制转换为double,可能会导致您正在使用的实现结果不一致。如果您以f为后缀,使编译器将其注册为float,则会得到0.00,这在double类型中是假的。未设置后缀的浮点数类型默认为double。
#include <stdio.h>
#include <float.h>
int main()
{
double d = 0.1;
float f = 0.1f;
printf("%f\n", (f > d));
return 0;
}
0.111 < 0.111111
,对吗?现在假设你正在计算6/9。0.667 > 0.666667
,对吗?你不能将6/9在三位小数中表示为0.666
,因为它不是最接近6/9的三位小数! - Eric Lippert