Internal representation of double

I came across a puzzle which I can not explain. The setup is SCO OpenServer 5.7 (32 bit OS) and native SCO compiler. double is 8 bytes long on this system. I am able to populate the double variable with two different sets of values that produces the same double value, please see below:

#include <stdio.h> 
#include <stdlib.h> 
main(int argc, char *argv[])
{
  unsigned char *ch = NULL;
  char  seq1[] = {   0,   0,    0, 128,  20, 174,  27,  64 };
  char  seq2[] = {174, 71, 225, 122,  20, 174,  27,  64 };
  const int sz = sizeof(double);
  int   i = 0;
  double d = 6.92;
        printf("d=%.6f\t:\t", d);
        ch = (unsigned char *)&d;
        for(i = 0; i < sz; i++)
        {
                printf("%u ", *ch++);
        }
        printf("\n");
        ch = (unsigned char *)&d; d = 0;
        for(i = 0; i < sz; i++)
                *ch++ = seq1;
        printf("1) d=%.6f\n", d);
        ch = (unsigned char *)&d; d = 0;
        for(i = 0; i < sz; i++)
                *ch++ = seq2;
        printf("2) d=%.6f\n", d);
        return(0);
}

and results are:

d=6.920000      :       174 71 225 122 20 174 27 64 
1) d=6.920000
2) d=6.920000

How is that possible?

The internal representation of floating-point numbers is a well-documented standard.

As for why two things can represent identical numbers, it's because of how the number works: A number times a base to an exponent. You could represent 256 with 1 * 2^8, or 2 * 2^7, etc.

Usually though these numbers would be normalized into the first, simplest form to ensure greatest accuracy in numeric operations.

Whatever compiler you are using is broken...get a newer one. Besides you are trying to print a double as a float with only 6 digits of precision so you can guess what happens if the exponent is a very large negative number and change the printf to

printf("d = %.6e", d);

The difference between float and double is nil as far as printf's concerned, they get promoted to double these days when they get passed as varargs.

Actually SEQ1 and SEQ2 are different IEEE 754 binary64 numbers according to my calculations with SEQ2 being the "correct" answer.

SEQ1: 0 0 0 0 128 20 174 27 64 = 401BAE1480000000 = 6.9200000762939450

Bit 62 (Sign bit) 0 (+)
Bits 62 - 52 (Exponent) 10000000001 = 1025 - 1023 = 2
Bits 51 - 0 (Significand) 1 .1011101011100001010010000000000000000000000000000000 = 1.7300000190734863

SEQ2: 174 71 225 122 20 174 27 64 = 401BAE147AE147AE = 6.9200000000000000

Bit 62 (Sign bit) 0 (+)
Bits 62 - 52 (Exponent) 10000000001 = 1025 - 1023 = 2
Bits 51 - 0 (Significand) 1 .1011101011100001010001111010111000010100011110101110 = 1.7300000000000000

The printf format precision (%.6f) masked the difference.

Except when it comes to the degree of precision desired...old compilers used to automatically convert floats to doubles but that is no longer practised and if that is indeed the case then all the more reason to use the double format specification instead of the float.

---------- Post updated at 02:59 PM ---------- Previous update was at 01:59 PM ----------

Its good you posted the results as I was getting confused why my systems were reporting different...so it is an endianness issue and here are the results from an AIX and HPUX machine which are both big endian.

d = 2.7178687957231882e-312    (0x0000008014ae1b40)     /*  seq1  */

d = -9.6037214055361180e-86    (0xae47e17a14ae1b40)     /*  seq2  */

I'm not saying float and double are the same. I'm saying %f is capable of handling double-precision numbers.

Consider that %f works with doubles and floats even though they're different sizes. stdargs has no way to warn printf which is used, so the compiler must be converting them to something consistent. So if double-precision works in printf at all, they're all being passed as doubles, and all being used as doubles, not floats.

All the implementations of printf that I have seen explicitly convert all floats to doubles in the code and do not leave it up to the compiler to decide whether to or not.