今天早上我有一个小小的WTF时刻。 WTF可以概括为:
float x = 0.2f;
float y = 0.1f;
float z = x + y;
assert(z == x + y); //This assert is triggered! (Atleast with visual studio 2008)
原因似乎是表达式x + y
被提升为double并与z
中的截断版本进行比较。 (如果我将z
更改为double
,则不会触发断言。)
我可以看到,出于精确原因,在将结果转换为单精度之前,以双精度执行所有浮点算术是有意义的。我在标准中找到了以下段落(我想我已经知道了,但不是在这种情况下):
4.6.1。
“类型float
的右值可以转换为double
类型的右值。值不变”
我的问题是,x + y
保证会被提升为加倍还是由编辑自行决定?
更新:由于很多人声称不应该使用==
作为浮点数,我只想说明在我正在使用的特定情况下,确切地说比较是合理的。
浮点比较 很棘手,这里有一个关于这个主题的有趣link,我认为还没有提到过。
答案 0 :(得分:14)
您通常不能假设==
将按预期的方式运行浮点类型。比较舍入值或使用abs(a-b) < tolerance
之类的结构。
升级完全由编译器自行决定(并取决于目标硬件,优化级别等)。
在这种特殊情况下发生的事情几乎可以肯定,值以比内存更高的精度存储在FPU寄存器中 - 通常,现代FPU硬件在程序员要求的精度内部以双精度或更高精度工作,编译器生成代码,以便在将值存储到内存时进行适当的转换;在未经优化的构建中,x+y
的结果在进行比较时仍然在寄存器中,但z
将被存储到内存中并被取回,因此被截断为浮点精度。 / p>
答案 1 :(得分:8)
Working draft for the next standard C++0x第5节第11点说
浮动操作数的值和浮动表达式的结果可以表示为比该类型所需的精度和范围更大的精度和范围;这些类型没有改变
因此由编制者自行决定。
答案 2 :(得分:1)
使用gcc 4.3.2,断言不被触发,实际上,从x + y
返回的右值是float
,而不是double
所以这取决于编译器。这就是为什么依靠两个浮点值之间的精确相等是不明智的。
答案 3 :(得分:1)
这是问题,因为浮点数到二进制转换不能提供准确的精度。
在sizeof(float)
字节内,它无法容纳浮点数的精确值,算术运算可能导致近似,因此相等性失败。
见下文,例如
float x = 0.25f; //both fits within 4 bytes with precision
float y = 0.50f;
float z = x + y;
assert(z == x + y); // it would work fine and no assert
答案 4 :(得分:1)
C ++ FAQ lite对该主题进行了进一步的讨论:
答案 5 :(得分:0)
我认为这是由编译人员自行决定的,但是你可以随时用强制转换强制它,如果这是你的想法?
答案 6 :(得分:-1)
永远不能直接比较花车的另一个原因。
if (fabs(result - expectedResult) < 0.00001)