为什么这两个代码变体产生不同的浮点结果?
Why does these two code variants produce different floating-point results?
给出这个示例c++代码片段:
void floatSurprise()
{
// these come from some sort of calculation
int a = 18680, b = 3323524, c = 121;
float m = float(a) / c;
// variant 1: calculate result from single expression
float r1 = b - (2.0f * m * a) + (m * m * c);
cout << "r1 = " << r1 << endl;
// variant 2: break up the expression into intermediate parts,
/// then calculate
float
r2_p1 = 2.0f * m * a,
r2_p2 = m * m * c,
r2 = b - r2_p1 + r2_p2;
cout << "r2 = " << r2 << endl;
}
输出为:
dev1 = 439703
Dev2 = 439702
在调试器中查看时,值实际上分别是439702.50和439702.25,这本身就很有趣-不确定为什么iostream默认打印没有小数部分的浮点数。EDIT:原因是cout的默认精度设置太低,需要cout <<设置精度(7)至少可以看到这个数量级的数字的小数点
但我更感兴趣的是为什么我得到不同的结果。我想它与舍入和一些微妙的int与所需的float输出类型的相互作用有关,但我不能指出它。哪个值是正确的?
我很惊讶,这么简单的一段代码竟然会搬起石头砸自己的脚。任何见解将非常感激!编译器为vc++ 2010。EDIT2:我使用电子表格进行了更多的调查,以生成中间变量的"正确"值,并发现(通过跟踪)它们确实被修剪,导致最终结果的精度损失。我还发现了单个表达式的问题,因为我实际上使用了一个方便的函数来计算平方,而不是m * m
:
template<typename T> inline T sqr(const T &arg) { return arg*arg; }
尽管我很好地问了,编译器显然没有内联这个,并且单独计算值,在将值返回给表达式之前修剪结果,再次扭曲结果。哎哟。
你应该看看我关于为什么同样的事情发生在c#中的长篇大论的回答:
(.1f+.2f==.3f) != (.1f+.2f). = (.3f)为什么?
总结一下:首先,使用float只能得到小数点后7位的精度。如果你在整个计算过程中进行精确的算术,正确的答案大约是439702.51239669……因此,考虑到浮点数的局限性,无论哪种情况,你都非常接近正确答案。
但这并不能解释为什么看起来完全相同的计算会得到不同的结果。答案是:编译器被允许在很大程度上使你的数学更精确,显然你遇到了两种情况,优化器采用逻辑上相同的表达式,但没有将它们优化到相同的代码。
无论如何,请仔细阅读我关于c#的回答;
- 这两个代码片段相似,但显示的结果不同
- 为什么第二个代码给出了预期的结果,而第一个代码却没有?
- RapidXML - 代码创建意外结果
- 代码未在联机编译器上显示结果
- 为什么这两段使用 constexpr、__PRETTY_FUNCTION__ 和 char * 的代码有不同的结果?
- C++位移位在相反方向上具有相同的常量,结果不同,代码更改很小
- 我的代码厨师提交显示错误的结果
- 如何查找导致结果不一致的代码
- 如何使用 c++ 将股票代码的结果添加到 poloniex 中的数组中?
- 我无法弄清楚我的代码未显示预期结果的问题
- 为什么这段代码(在 Matlab 的 MEX 文件中使用 OpenMP)给出不同的结果?
- 为什么这两个相似的代码计算行列式的结果不同?
- 为什么这个C++程序在代码::块和在线 IDE 之间返回不同的结果?
- C++:汇编代码包含断言结果
- 有人可以解释一下'sizeof'在此代码中返回的结果吗
- 相同的代码在联机 IDE 和本地 IDE 中给出不同的结果
- 来自 open() 的返回结果代码之间的差异:25 与 3
- 我不小心没有使用std::ref——结果代码做了什么?
- Typename给出了奇怪的输入结果代码块
- 用c++运行一个进程,过滤输出,获取结果代码,同时获取系统错误