0

I put positive value into variable which type is double. And when I saw the contents of the memory, the sign bit was 1. I think it should be 0, but it may be wrong. Why the sign bit was 1?

#include<stdio.h>

int main(){
    int num=116;

    union {float x; int i;} u4;
    union {double x; long long int i;} u8;

    u4.x = (float) num/10000.0;
    u8.x = (double) num/10000.0;

    printf("%lx\n",u4.i);
    printf("%lx\n",u8.i);

    printf("%.30f\n",u4.x);
    printf("%.30f\n",u8.x);

    return 0;
}

Output:

3c3e0ded
a5119ce0
0.011599999852478504000000000000
0.011599999999999999000000000000

a5119ce0 is double type's output. a5119ce0 means
1010 0101 0001 0001 1001 1100 1110 0000
and it's sign bit is 1 which means this is negative number.

3c3e0ded is float type's output. 3c3e0ded means
0011 1100 0011 1110 0000 1101 1110 1101

I'm also puzzled that the same length results were obtained with float and double.

eesiraed
  • 4,626
  • 4
  • 16
  • 34
invalid
  • 1,215
  • 4
  • 13
  • 30

3 Answers3

3

If you compile with warnings on, your compiler will probably point out your mistake. Eg, with gcc:

$ gcc -Wall main.c 

main.c: In function ‘main’:
main.c:12:12: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘int’ [-Wformat=]
     printf("%lx\n",u4.i);
            ^
main.c:12:12: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘int’ [-Wformat=]
main.c:13:12: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘long long int’ [-Wformat=]
     printf("%lx\n",u8.i);
            ^
main.c:13:12: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘long long int’ [-Wformat=]

Making the corrections indicated:

printf("%x\n",u4.i);
printf("%llx\n",u8.i);

Compiles with no warnings and produces the expected output:

3c3e0ded
3f87c1bda5119ce0
0.011599999852478504180908203125
0.011599999999999999200639422270
Heath Raftery
  • 3,643
  • 17
  • 34
2

You're printing it wrong. Remember, C assumes nothing about the inputs to printf. You're specifying %lx to print the double, effectively printing only the first 32 bytes. You should use %ll to print 64 bit integers (known in C as long long).

To understand which 32 bits of the double is printed, you must also understand big-endian and little-endian. Basically, different CPU's put the least significant bit either first or last, which means that depending on the target CPU, you'll either get the least significant 32 bits or the most significant. x86 and x64 (which you're likely to be running your code on) are little endian BTW.

Arshia001
  • 1,854
  • 14
  • 19
  • 3
    But there *is*: you're effectively copying 64 bits of data on the stack for printf to read. It's going to read 32 bits to print a long. Which 32 bits it gets depends on which 32 bits come first. – Arshia001 Apr 14 '19 at 06:27
  • Indeed using the specifier `%llx` causes `3f87c1bda5119ce0` to be output, which is positive and shows the machine is little-endian. Had it been big-endian, the sign bit would have been correct. – Weather Vane Apr 14 '19 at 06:27
  • Thank you!! your answer is very helphul for me! – invalid Apr 14 '19 at 06:30
  • @user703016 "undefined" doesn't mean magic you know. As long as you understand the underlaying OS/hardware and how it works, you can make educated guesses as to what happens. In this case, it's a matter of understanding CPU endianness, function calls and variable argument lists; but then again, coding in C is all about understanding the OS and hardware anyway. If you ever get it print anything other than either half of the double, I'd love to see the code. – Arshia001 Apr 14 '19 at 06:45
2

Here's a modified version of your code to print out the floating point value in IEEE-754 format as its stored in memory. Notice the quick and dirty test for big-endian vs little-endian, and that influences how we print:

int main()
{
    unsigned int test = 0x01020304;
    int isBigEndian = (1 == *(char*)&test);
    float num = 3.14;

    // breaking the rules here, but this will work for demonstration purposes
    unsigned char* ptr = (unsigned char*)&num

    // we're assuming sizeof(float) == 4
    if (isBigEndian)
        printf("0x%02x%02x%02x%02x\n", ptr[0], ptr[1], ptr[2], ptr[3]);
    else
        printf("0x%02x%02x%02x%02x\n", ptr[3], ptr[2], ptr[1], ptr[0]);

    return 0;

}
selbie
  • 100,020
  • 15
  • 103
  • 173