3

I have this code:

double A = doSomethingWonderful(); // same as doing  A = 0;
if(A == 0)
{
    fprintf(stderr,"A=%llx\n", A);
}

and this output:

A=7f35959a51c0

how is this possible?

I checked the value of 7f35959a51c0 and seems to be something like 6.91040329973658785751176861252E-310, which is very small, but not zero.

EDIT:

Ok I understood that that way of printing the hex value for a double is not working. I need to find a way to print the bytes of the double.

Following the comments I modified my code:

    A = doSomethingWonderful();// same as doing  A = 0;
    if(A == 0)
    {
        char bytes[8];
        memcpy(bytes, &A, sizeof(double));
        for(int i = 0; i < 8; i++)
        fprintf(stderr," %x", bytes[i]);
    }

and I get this output:

0 0 0 0 0 0 0 0

So finally it seems that the comparison is working properly but I was doing a bad print.

HAL9000
  • 3,562
  • 3
  • 25
  • 47
  • Possible duplicate of [How dangerous is it to compare floating point values?](http://stackoverflow.com/questions/10334688/how-dangerous-is-it-to-compare-floating-point-values) – HadeS Oct 25 '15 at 14:06
  • @HadeS is not a duplicate, I perfectly know that is dangerous. :) – HAL9000 Oct 25 '15 at 14:08
  • 2
    Did you mean to use %llx and not %f to print the value? AFAIK using %llx to print a double is undefined behavior. – Jeremy Friesner Oct 25 '15 at 14:09
  • @JeremyFriesner: the point is that zero in hex is 0x0000000000000000. I want to trigger this value and only this value. The printf should only print 0x0000000000000000 in any possible case. If A has non zero bits, it shouldn't even go in the branch. – HAL9000 Oct 25 '15 at 14:11
  • Dunno about the language-lawyer aspect, but as a quick work-around, if (memcmp(&A, 0, sizeof(A))==0) would do the trick. Btw IIRC there may be double values that mean zero but are not all zero-bits. – Jeremy Friesner Oct 25 '15 at 14:14
  • @HAL9000 Nonetheless printing a double value with llx is undefined. You can convert it manually to an 8byte integer first. – deviantfan Oct 25 '15 at 14:15

2 Answers2

6

IEEE 754 precision floating point values use a bias in the exponent value in order to fully represent both positive and negative exponents. For double-precision values, that bias is 1023[source], which happens to be 0x3ff in hex, which matches the hex value of A that you printed for 1, or 0e0.


Two other small notes:

  • When printing bytes, you can use %hhx to get it to only print 2 hex digits instead of sign-extending to 8.
  • You can use a union to reliably print the double value as an 8-byte integer.
double A = 0;
if(A == 0)
{
    A = 1; // Note that you were setting A to 1 here!
    char bytes[8];
    memcpy(bytes, &A, sizeof(double));
    for(int i = 0; i < 8; i++)
        printf(" %hhx", bytes[i]);
}
int isZero;
union {
    unsigned long i;
    double d;
} u;
u.d = 0;
isZero = (u.d == 0.0);
printf("\n============\n");
printf("hex   = %lx\nfloat = %f\nzero?   %d\n", u.i, u.d, isZero);

Result:

 0 0 0 0 0 0 f0 3f
============
hex   = 0
float = 0.000000
zero?   1

So in the first line, we see that 1.0 is 0e0 (i.e., 00).

In the following lines, we see that when you use a union to print the hex value of the double 0.0, you get 0 as expected.

DaoWen
  • 32,589
  • 6
  • 74
  • 101
2

When you pass your double to printf(), you pass it as a floating point value. However, since the "%x" format is an integer format, your printf() implementation will try to read an integer argument. Due to this fundamental type mismatch, it is possible, for instance, that the calling code places your double value in a floating point register, while the printf() implementation tries to read it from an integer register. Details depend on your ABI, but apparently the bits that you see are not the bits that you passed. From a language point of view, you have undefined behavior the moment that you have a type mismatch between one printf() argument and its corresponding format specification.

Apart from that, +0.0 is indeed represented as all bits zero, both in single and in double precision formats. However, this is only positive zero, -0.0 is represented with the sign bit set.

In your last bit of code, you are inspecting the bit pattern of 1.0, because you overwrite the value of A before you do the conversion. Note also that you get fffffff0 instead of f0 for the seventh byte because of sign extension. For correct output, use an array of unsigned bytes.

The pattern that you are seeing decodes like this:

00 00 00 00 00 00 f0 3f
to big endian:
3f f0 00 00 00 00 00 00

decode fields:
sign: 0 (1 bit)
exponent: 01111111111 (11 bit), value = 1023
    exponent = value - bias = 1023 - 1023 = 0
mantissa: 0...0 (52 bit), value with implicit leading 1 bit: 1.0000...

entire value: -1^0 * 2^0 * 1.0 = 1.0
cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106