tags:

views:

598

answers:

7

I want to learn how the computer represents the double type in bit, but the & and | bit operators can't use double. And memcpy(&d, &src, 8) also doesn't seem to work. Any suggestions?

+5  A: 

Here:

#include <stdio.h>
int main ()
{
    double decker = 1.0;
    unsigned char * desmond = (unsigned char *) & decker;
    int i;

    for (i = 0; i < sizeof (double); i++) {
         printf ("%02X ", desmond[i]);
    }
    printf ("\n");
    return 0;
}

You can try it: http://codepad.org/onHnAcnC

Kinopiko
Shouldn't that `8` be `sizeof(double)`?
sbi
Thanks for your correction.
Kinopiko
sbi: Should, but should also pose no problem, as `double` is defined to be 64 bits long. Ok, architectures with non-8-bit bytes are another matter, then :-)
Joey
+1 for the variable names, despite the spelling error (it's Dekker).
fvu
@Johannes Rössel: The ISO standard for C defines no such thing. It merely requires that every float can be exactly represented as a double, and that every double can be exactly represented as a long double. That merely implies that sizeof double is at least equal to sizeof float. An implementation is free to use whatever size. On machines with and FPU, it is likely that the size is determined by the hardware implementation. In the 16-bit dsPIC compiler I am currently using double is 32-bits unless you specify 64-bits with a compiler option.
Clifford
Possibly Johannes is confusing Standard C with IEEE 754. The latter does define a double-precision floating point as 64 bits. C's "double" doesn't have to be 754's "double-precision", but they have similar names for a reason.
Steve Jessop
@fvu: no it's double DECKER not DEKKER.
Kinopiko
But (perhaps confusingly), it's Desmond Dekker: http://en.wikipedia.org/wiki/Desmond_Dekker
Steve Jessop
A: 

That isn't going to be very enlightening unless you also know a bit about typical IEEE FP representations.

Most likely the way your machine represents doubles is spelled out here.

T.E.D.
+1  A: 

A particular bit layout by itself is meaningless. Suppose I have the following: 1101

Maybe I say that is unsigned and it represents the value 13.

Maybe it is signed and that high bit signifies that the value is a negative which means it is now -5.

Consider further that I consider the high two bits to be a base and the low two bits to be an exponent, then I get the value 3.

You see, it isnt the storage, its the interpretation. Read up on how floating point values are represented and interpreted; it will serve you much better than seeing how the bits are packed.

ezpz
A: 

This works for me

#include <stdio.h>
#include <string.h> /* memmove */
int main(void) {
  unsigned char dontdothis[sizeof (double)];
  double x = 62.42;

  printf("%f\n", x);
  memmove(&dontdothis, &x, sizeof dontdothis);
  /* examine/change the array dontdothis */
  dontdothis[sizeof x - 1] ^= 0x80;
  /* examine/change the array dontdothis */
  memmove(&x, &dontdothis, sizeof dontdothis);
  printf("%f\n", x);
  return 0;
}

The result is

62.420000
-62.420000
pmg
A: 

The key is to convert the double to a long long (assuming sizeof(double) == sizeof(long long)) without changing binary representation. This can be achieved by one of the following methods:

  • cast: double a; long long b = *((long long *)&a);
  • union: union { double a ; long long b };
mouviciel
The cast has undefined result, because of type-punning and the strict aliasing rules. Sometimes this kind of thing actually does go wrong, and with enough optimisation some compilers will read b before a has been initialised (assuming it is initialised), although you might be OK with a case this simple. Converting through a union is undefined behaviour (except in certain cases of which this isn't one), but in practice no sensible compiler writer will let it break, since too much code uses it.
Steve Jessop
Wrong - gcc in release build will break your code!
Jens
A: 

Another option is to use bitfields. Armed with such a structure and knowledge of how a double is supposed to be stored on your computer you can very easily print out the different parts of the internal representation of the double. A bit like they do here.

fvu
+2  A: 
union {
  double  d;
  unsigned char c[sizeof(double)];
} d;

int main(int ac, char **av) {
  int i;
  char s1[80], s2[80];

  d.d = 1.0;
  for(i = 0; i < sizeof d; ++i) {
    sprintf(s1 + i * 3, " %02x", d.c[i]);
    sprintf(s2 + i * 3, " %02x", d.c[sizeof d - 1 - i]);
  }
  printf("%s\n%s\n", s1, s2);
  return 0;
}

$ ./a.out
00 00 00 00 00 00 f0 3f
3f f0 00 00 00 00 00 00

Or you could just read about the IEEE 754 standard, which specifies representation.

http://en.wikipedia.org/wiki/IEEE%5F754-1985

DigitalRoss