Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicitly casting a float constant

Please have a look at this code:

#include <stdio.h>

int main(void)
{
    short s;
    int i = 65696;
    float f = 65696.0F;

    printf("sizeof(short) = %lu\n", sizeof(short));

    s = i;
    printf("s = %hd\n", s);
    s = f;
    printf("s = %hd\n", s);

    s = 65696;
    printf("s = %hd\n", s);
    s = 65696.0F;
    printf("s = %hd\n", s);

    return 0;
}  

It gave output as :

sizeof(short) = 2
s = 160
s = 160
s = 160
s = 32767

In last line why it's 32767 and not 160 ? What's the difference between saying f = 65696.0F; s = f; and s = 65696.0F; ?

like image 663
rootkea Avatar asked Feb 08 '13 14:02

rootkea


2 Answers

Because if the integral part of the float value is not representable in the new type, the conversion is undefined behavior.

In your case, SHRT_MAX is probably 32767 and so the integral part of 65696.0F is then not representable in a short object.

like image 135
ouah Avatar answered Oct 02 '22 18:10

ouah


This is "undefined behaviour," which means that the compiler is free to do what it wants. But "undefined" does not mean "unexplainable."

What the compiler is doing in the case of s = f is converting f first to the int value 65696, and then assigning 65696 to s, which overflows and leaves 160. The compiler does this because there is a CPU instruction to convert a floating point number into a 32-bit integer, but not directly into a 16-bit integer

What the compiler is doing with s = 65696.0F is simpler: It knows that 65696.0 is out of range, so it assigns the highest value available to s, which happens to be 2^15-1 = 32767.

You can verify this if you read the assembly code the compiler generates for s = f (for example using -S switch with gcc):

    movss   -4(%rbp), %xmm0        # Load float from memory into register xmm0
    cvttss2si       %xmm0, %eax    # Convert float in xmm0 into signed 32 bit, store in eax
    movw    %ax, -10(%rbp)         # Store lower 16 bits of eax into memory
    movswl  -10(%rbp), %eax        # Load those 16 bits into eax, with sign extend

The last instruction clobbers the high 16 bits of %eax, setting it to all 0s in this case.

What it generates for s = 65696.0F is simpler:

    movw    $32767, -10(%rbp)      # Store the lower 16 bits of 32767 into memory
    movswl  -10(%rbp), %eax        # Load those 16 bits into eax, with sign extend
like image 38
Joni Avatar answered Oct 02 '22 18:10

Joni