5

I'd like to get an exact/precise answer why the following code prints different results:

#include "stdio.h" int main(void) { int a = 9; int b = 10; printf("%d\n", (double)a / (double)b == 0.9); /* prints 0 */ printf("%d\n", (double)9 / (double)10 == 0.9); /* prints 1 */ return 0; } 

I think this might be compiler dependent, mine is gcc (GCC mingw Windows7) 4.8.1 and gcc (Debian 4.7.2-5) 4.7.2.

Thank you very much!

UPDATE!

I generated the assembly codes with and without the -std=c99 option, this should be helping to understand what is happening here.

Without -std=c99 (this gives the result 0/1):

 .file "a.c" .section .rodata.str1.1,"aMS",@progbits,1 .LC0: .string "%d\n" .section .text.startup,"ax",@progbits .p2align 4,,15 .globl main .type main, @function main: .LFB11: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $-16, %esp subl $16, %esp movl $1, 4(%esp) movl $.LC0, (%esp) call printf movl $1, 4(%esp) movl $.LC0, (%esp) call printf xorl %eax, %eax leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE11: .size main, .-main .ident "GCC: (Debian 4.7.2-5) 4.7.2" .section .note.GNU-stack,"",@progbits 

With -std=c99 (this gives the result 1/1):

 .file "a.c" .section .rodata .LC1: .string "%d\n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $-16, %esp subl $32, %esp movl $9, 28(%esp) movl $10, 24(%esp) fildl 28(%esp) fildl 24(%esp) fdivrp %st, %st(1) movl $1, %edx fldt .LC0 fucomp %st(1) fnstsw %ax sahf jp .L5 fldt .LC0 fucompp fnstsw %ax sahf je .L2 jmp .L3 .L5: fstp %st(0) .L3: movl $0, %edx .L2: movzbl %dl, %eax movl %eax, 4(%esp) movl $.LC1, (%esp) call printf movl $1, 4(%esp) movl $.LC1, (%esp) call printf movl $0, %eax leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE0: .size main, .-main .section .rodata .align 16 .LC0: .long 1717986918 .long -429496730 .long 16382 .ident "GCC: (Debian 4.7.2-5) 4.7.2" .section .note.GNU-stack,"",@progbits 
11
  • This cannot be true... Could your print a and b as integers and check if they are 9 and 10? This website also claims to be using the same compiler, and it prints 1 for both, like it should. Commented Mar 28, 2014 at 14:44
  • Prints 1/1 for gcc 4.8.2 Commented Mar 28, 2014 at 14:47
  • 1
    Since floating point math isn't exact (and compilers store temporary results with different precision), I'd expect such behavior. But I also get 1/1 on gcc (x64). Commented Mar 28, 2014 at 14:53
  • 6
    You may be able to get more information by directly printing the two division results, to high precision, rather than by comparing them. My guess is that, with your compiler, the one with literals is being evaluated at compile time, the one with variables is evaluated at run time, and the results have different precisions. Commented Mar 28, 2014 at 14:55
  • @ThoAppelsin: I spent a half day to find out this was causing problems to my program. I didn't believe it eighter, but it is true. Further information to the 4.8.1 compiler: it comes from MinGW on Windows7 (i don't know if it matters). Commented Mar 28, 2014 at 15:12

1 Answer 1

3

In C, floating point math is allowed to run in higher precision than the code indicates.
Especially the compile time math (2nd line) may run as a long double

C11dr §5.2.4.2.2 9 "Except for assignment and cast (which remove all extra range and precision), the values yielded by operators with floating operands and values subject to the usual arithmetic conversions and of floating constants are evaluated to a format whose range and precision may be greater than required by the type.

See @Patricia Shanahan above.


[Edit]

Check the FP evaluation mode, if defined

#include <float.h> printf("%d\n", FLT_EVAL_METHOD); 

C11dr §5.2.4.2.2 9 (cont.) The use of evaluation formats is characterized by the implementation-defined value of FLT_EVAL_METHOD.

-1 indeterminable;

0 evaluate all operations and constants just to the range and precision of the type;

1 evaluate operations and constants of type float and double to the range and precision of the double type, evaluate long double operations and constants to the range and precision of the long double type;

2 evaluate all operations and constants to the range and precision of the long double type.

All other negative values for FLT_EVAL_METHOD characterize implementation-defined behavior.

Sign up to request clarification or add additional context in comments.

4 Comments

Nice. -std=c11 enables the macro (which is 2) and prints 1/1. g++ -std=c++11 still prints 0/1 though, and the macro is undefined.
GCC implements C99 (see gcc.gnu.org/ml/gcc-patches/2008-11/msg00105.html ) but sadly, does not activate it unless explicitly demanded by the programmer. Use -std=c99 or -std=c11 to activate C99-compliant FLT_EVAL_METHOD semantics in GCC.
@Peter Schneider Thanks. I suspect it is possible to have c11 and various FLT_EVAL_METHOD settings too. I wonder what OP's FLT_EVAL_METHOD setting ?
@Pascal Cuoq: Right, -std=c99 does it already. What it does, according to the docs, is to switch on -fexcess-precision=standard: "When compiling C, if ‘-fexcess-precision=standard’ is specified then excess precision follows the rules specified in ISO C99; in particular, both casts and assignments cause values to be rounded to their semantic types" (p.128 in the PDF (gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc.pdf), as opposed to assignments only with -ffloat-store).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.