我有以下一些代码,但是当使用GCC 4.4使用各种优化标志进行编译时,我在运行时会得到一些意想不到的结果.
#include <iostream>
int main()
{
const unsigned int cnt = 10;
double lst[cnt] = { 0.0 };
const double v[4] = { 131.313, 737.373, 979.797, 731.137 };
for(unsigned int i = 0; i < cnt; ++i) {
lst[i] = v[i % 4] * i;
}
for(unsigned int i = 0; i < cnt; ++i) {
double d = v[i % 4] * i;
if(lst[i] != d) {
std::cout << "error @ : " << i << std::endl;
return 1;
}
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译时使用:"g ++ -pedantic -Wall -Werror -O1 -o test test.cpp"我得到以下输出:"error @:3"
编译时使用:"g ++ -pedantic -Wall -Werror -O2 -o test test.cpp"我得到以下输出:"error @:3"
编译时使用:"g ++ -pedantic -Wall -Werror -O3 -o test test.cpp"我没有错误
编译时使用:"g ++ -pedantic -Wall -Werror -o test test.cpp"我没有错误
我不认为这是与舍入有关的问题,或者比较中的epsilon差异.我已经尝试过使用英特尔v10和MSVC 9.0,它们似乎都按预期工作.我相信这应该只是一点点比较.
如果我用以下内容替换if语句: if (static_cast<long long int>(lst[i]) != static_cast<long long int>(d))
,并添加"-Wno-long-long",运行时我在任何优化模式下都没有错误.
如果我std::cout << d << std::endl;
在"返回1"之前添加,我在运行时的任何优化模式都没有错误.
这是我的代码中的错误,或者GCC是否有问题以及它处理双重类型的方式?
注意:我刚刚尝试使用gcc版本4.3和3.3,但没有显示错误.
解决方案: Mike Dinsdale注意到以下错误报告:http : //gcc.gnu.org/bugzilla/show_bug.cgi?id = 323 看来GCC团队并不完全确定问题的性质.
正如错误报告中所建议的那样,可能的解决方案是使用ffloat-store选项.我已经尝试了这个并且它有效,但是从性能的角度来看,结果并不是很好,尽管ymmv.
结果取决于优化设置这一事实表明它可能是x87扩展精度混乱的东西(如Michael Burr所说).
这是我使用的一些代码(在x86处理器上使用gcc)来关闭扩展精度:
static const unsigned int PRECISION_BIT_MASK = 0x300;
///< bitmask to mask out all non-precision bits in the fpu control word \cite{INTEL}.
static const unsigned int EXTENDED_PRECISION_BITS = 0x300;
///< add to the fpu control word (after zeroing precision bits) to turn on extended precision \cite{INTEL}.
static const unsigned int STANDARD_PRECISION_BITS = 0x200;
///< add to the fpu control word (after zeroing precision bits) to turn off extended precision \cite{INTEL}.
void set_fpu_control_word(unsigned int mode)
{
asm ("fldcw %0" : : "m" (*&mode));
}
unsigned int get_fpu_control_word()
{
volatile unsigned int mode = 0;
asm ("fstcw %0" : "=m" (*&mode));
return mode;
}
bool fpu_set_extended_precision_is_on(bool state)
{
unsigned int old_cw = get_fpu_control_word();
unsigned int masked = old_cw & ~PRECISION_BIT_MASK;
unsigned int new_cw;
if(state)
new_cw = masked + EXTENDED_PRECISION_BITS;
else
new_cw = masked + STANDARD_PRECISION_BITS;
set_fpu_control_word(new_cw);
return true;
}
bool fpu_get_extended_precision_is_on()
{
unsigned int old_cw = get_fpu_control_word();
return ((old_cw & PRECISION_BIT_MASK) == 0x300);
}
Run Code Online (Sandbox Code Playgroud)
或者您可以使用valgrind运行代码,它不会模拟80位寄存器,对于像这样的短程序来说可能更容易!
该问题可能是由于在存储表达式结果时丢失了一些精度,而编译器没有在本地存储表达式结果作为优化:
double d = v[i % 4] * i; // the result, `d`, might be kept in a register
// instead of storing it in a memory location,
// keeping full precision
if(lst[i] != d) { // the value stored in lst[i] may have lost some
// precision since it had to be stored in memory,
// which might not be able to hold the full
// precision that the expression generated
Run Code Online (Sandbox Code Playgroud)
C99标准在6.3.1.8/2“常用算术转换”中说:
浮点操作数的值和浮点表达式的结果可以用比类型所要求的更高的精度和范围来表示;类型不会因此改变。