Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Floating point number comparison via integer casting in C language

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
like image 216
Barna Bajak Avatar asked Oct 20 '22 11:10

Barna Bajak


1 Answers

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.

like image 128
chux - Reinstate Monica Avatar answered Oct 23 '22 01:10

chux - Reinstate Monica