我有以下一些代码,但是当使用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;
}
编译时使用: “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” 我没有错误
< / LI>编译时: “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”,我就不会出现任何错误运行时的优化模式。
如果我在“返回1”之前添加 std::cout << d << std::endl;
,我在运行时的任何优化模式都不会出错。
这是我的代码中的错误,还是GCC有问题以及它处理double类型的方式?
注意:我刚刚尝试使用gcc版本4.3和3.3,但没有显示错误。
解决方案:Mike Dinsdale注意到以下错误报告:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 GCC团队似乎并非完全确定关于问题的性质。
正如错误报告中所建议的,可能的解决方案是使用ffloat-store选项。我已经尝试了这个并且它有效,但是从性能的角度来看,结果并不是很好,尽管ymmv。
答案 0 :(得分:7)
结果取决于优化设置这一事实表明它可能是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);
}
或者您可以使用valgrind运行您的代码,它不会模拟80位寄存器,对于像这样的短程序来说可能更容易!
答案 1 :(得分:4)
问题可能是在存储表达式的结果时丢失一些精度的结果,而编译器在本地作为优化时没有这样做:
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
C99标准在6.3.1.8/2“常用算术转换”中说:
浮动操作数的值和浮动表达式的结果可以是 代表的精度和范围比该类型所要求的更高;类型不是 由此改变了。
答案 2 :(得分:3)
x86中浮点寄存器的宽度与RAM中double
的宽度不同。因此,比较可能成功或失败,完全取决于编译器如何决定优化浮点值的负载。