Bea*_*ear 103 c++ optimization g++ c++-faq
以下代码适用于Visual Studio 2008,有无优化.但它只适用于没有优化的G ++(O0).
#include <cstdlib>
#include <iostream>
#include <cmath>
double round(double v, double digit)
{
double pow = std::pow(10.0, digit);
double t = v * pow;
//std::cout << "t:" << t << std::endl;
double r = std::floor(t + 0.5);
//std::cout << "r:" << r << std::endl;
return r / pow;
}
int main(int argc, char *argv[])
{
std::cout << round(4.45, 1) << std::endl;
std::cout << round(4.55, 1) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
输出应该是:
4.5
4.6
Run Code Online (Sandbox Code Playgroud)
但是带有优化(O1- O3)的g ++ 将输出:
4.5
4.5
Run Code Online (Sandbox Code Playgroud)
如果我volatile在t之前添加关键字,它可以工作,那么可能存在某种优化错误吗?
测试g ++ 4.1.2和4.4.4.
以下是ideone的结果:http://ideone.com/Rz937
我在g ++上测试的选项很简单:
g++ -O2 round.cpp
Run Code Online (Sandbox Code Playgroud)
更有趣的结果,即使我/fp:fast在Visual Studio 2008上打开选项,结果仍然是正确的.
进一步问题:
我想知道,我应该总是打开-ffloat-store选项吗?
因为我测试的g ++版本附带了CentOS/Red Hat Linux 5和CentOS/Redhat 6.
我在这些平台下编译了很多程序,我担心它会在我的程序中引起意外的错误.调查我的所有C++代码并使用库是否有这样的问题似乎有点困难.有什么建议吗?
是否有人对甚至/fp:fast打开的原因感兴趣,Visual Studio 2008仍然有效?看起来Visual Studio 2008在这个问题上比g ++更可靠吗?
Max*_*kin 87
Intel x86处理器内部使用80位扩展精度,而double通常为64位宽.不同的优化级别会影响CPU的浮点值保存到内存中的频率,从而将其从80位精度舍入到64位精度.
使用-ffloat-storegcc选项可以获得具有不同优化级别的相同浮点结果.
或者,使用long doublegcc上通常为80位宽的类型,以避免从80位到64位精度的舍入.
man gcc 说的一切:
-ffloat-store
Do not store floating point variables in registers, and inhibit
other options that might change whether a floating point value is
taken from a register or memory.
This option prevents undesirable excess precision on machines such
as the 68000 where the floating registers (of the 68881) keep more
precision than a "double" is supposed to have. Similarly for the
x86 architecture. For most programs, the excess precision does
only good, but a few programs rely on the precise definition of
IEEE floating point. Use -ffloat-store for such programs, after
modifying them to store all pertinent intermediate computations
into variables.
Run Code Online (Sandbox Code Playgroud)
Dav*_*men 10
输出应该是:4.5 4.6如果您具有无限精度,或者您正在使用使用基于十进制而不是基于二进制的浮点表示的设备,那么输出将是什么.但是,你不是.大多数计算机使用二进制IEEE浮点标准.
正如Maxim Yegorushkin在他的回答中已经提到的,问题的一部分是你的计算机内部正在使用80位浮点表示.不过,这只是问题的一部分.该问题的基础是任何数量的n.nn5形式都没有精确的二进制浮点表示.那些极端情况总是不精确的数字.
如果你真的希望你的舍入能够可靠地围绕这些极端情况,你需要一个舍入算法来解决n.n5,n.nn5或n.nnn5等(但不是n.5)总是这样的事实不精确.找到确定某个输入值是向上还是向下舍入的角点大小写,并根据与此角点大小写的比较返回向上舍入或向下舍入的值.而且你需要注意优化编译器不会将找到的极移情况放在扩展精度寄存器中.
请参阅Excel如何成功舍入浮动数字,即使它们不精确?对于这样的算法.
或者你可以忍住这样一个事实,即角落的情况有时会错误地绕过.
不同的编译器具有不同的优化设置.根据IEEE 754,其中一些更快的优化设置不保持严格的浮点规则.Visual Studio中有一个特定的设置,/fp:strict,/fp:precise,/fp:fast,其中/fp:fast违反了什么可以做标准.您可能会发现此标志控制此类设置中的优化.您可能还会在GCC中找到类似的设置来更改行为.
如果是这种情况,那么编译器之间唯一不同的是GCC会在更高优化时默认寻找最快的浮点行为,而Visual Studio不会更高优化级别的浮点行为.因此,它可能不一定是一个真正的错误,但你不知道你正在打开的选项的预期行为.