gcc -v output: Reading specs from /opt/gcc-3.4/lib/gcc/i686-pc-linux-gnu/3.4.3/specs Configured with: ./configure --prefix=/opt/gcc-3.4 --enable-languages=c,c++ Thread model: posix gcc version 3.4.3 See following preprocessed output: extern "C" { extern void __assert_fail (__const char *__assertion, __const char *__file, unsigned int __line, __const char *__function) throw () __attribute__ ((__noreturn__)); } static void DoTest() { double doubleOne = 1.0; float floatOne = 1.0F; unsigned int *intDptr = (unsigned int *) &doubleOne; unsigned int *intFptr = (unsigned int *) &floatOne; (static_cast<void> (__builtin_expect (!!((intDptr[1] == 0x3ff00000) && (intDptr[0] == 0)), 1)? 0 : (__assert_fail ("(intDptr[1] == 0x3ff00000) && (intDptr[0] == 0)", "fptest.cpp", 12, __PRETTY_FUNCTION__), 0))); (static_cast<void> (__builtin_expect (!!(*intFptr == 0x3f800000), 1) ? 0 : (__assert_fail ("*intFptr == 0x3f800000", "fptest.cpp", 13, __PRETTY_FUNCTION__), 0))); } int main() { DoTest(); } This compiles fine up to -O1, but with -O2 the test fails. When looking at the generated assembler it seems to me that the compiler first issues the compare before putting a value in it. Generated assembler: gcc -O2 -S -o fptest.S fptest.cpp main: .LFB3: pushl %ebp .LCFI0: fld1 movl %esp, %ebp .LCFI1: subl $24, %esp .LCFI2: andl $-16, %esp subl $16, %esp cmpl $1072693248, -4(%ebp) fstpl -8(%ebp) jne .L4 movl -8(%ebp), %eax testl %eax, %eax jne .L4 leave xorl %eax, %eax ret .p2align 4,,7 .L4: movl $.LC2, (%esp) movl $_ZZ6DoTestvE19__PRETTY_FUNCTION__, %ecx movl $12, %edx movl %ecx, 12(%esp) movl $.LC1, %eax movl %edx, 8(%esp) movl %eax, 4(%esp) call __assert_fail Specifying volatile for all the local variables fixes it.
You can't access a floating point variable through a pointer to integer. Read up on -fstrict-aliasing, or its negative -fno-strict-aliasing. W.
This is called a violation of aliasing rules by the way.
I read the documentation on -fstrict-aliasing and it makes sense to me why this code breaks those rules. I just wonder why the compiler didn't warn about it, since this seems to be a pretty straight forward case of aliasing happening. Thanks for responding so quickly.
-Wstrict-aliasing can catch some, but you should not rely on it, because it can't possibly catch all of them. You should read and understand how aliasing works in ISO C and keep it in mind while writing the code.
Ok, thank you for your explanation. We have a huge codebase and we just increased the optimization level from -O to -O2 (together with -march=pentium3), that's when this came up. Since most of this code has been around for a long time (and not written by me) there is no way to guarantee that I caught all these cases. So I guess I'll just have to disable the strict aliasing option for now. Thanks for all your help.
The problem with the compiler not warning about these cases is that it is perfectly legal to cast a double* to an int* -- the problem is that it is not legal to access a double through an int*, but to flag this as a warning would mean that the compiler has to keep track of what each pointer points to, a task it cannot do in general. We just have to ask our users to write decent code... W.
Reopening to ...
Mark as a dup of bug 21920. *** This bug has been marked as a duplicate of 21920 ***