Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use bitwise operators on a "double" on C++?

I was asked to get the internal binary representation of different types in C. My program currently works fine with 'int' but I would like to use it with "double" and "float". My code looks like this:

template <typename T>
string findBin(T x) {
string binary;
for(int i = 4096 ; i >= 1; i/=2) {
        if((x & i) != 0) binary += "1";
        else binary += "0";
}
    return binary;
}

The program fails when I try to instantiate the template using a "double" or a "float".

like image 831
Juan Pablo Santos Avatar asked Jan 20 '11 03:01

Juan Pablo Santos


4 Answers

Succinctly, you don't.

The bitwise operators do not make sense when applied to double or float, and the standard says that the bitwise operators (~, &, |, ^, >>, <<, and the assignment variants) do not accept double or float operands.

Both double and float have 3 sections - a sign bit, an exponent, and the mantissa. Suppose for a moment that you could shift a double right. The exponent, in particular, means that there is no simple translation to shifting a bit pattern right - the sign bit would move into the exponent, and the least significant bit of the exponent would shift into the mantissa, with completely non-obvious sets of meanings. In IEEE 754, there's an implied 1 bit in front of the actual mantissa bits, which also complicates the interpretation.

Similar comments apply to any of the other bit operators.

So, because there is no sane or useful interpretation of the bit operators to double values, they are not allowed by the standard.


From the comments:

I'm only interested in the binary representation. I just want to print it, not do anything useful with it.

This code was written several years ago for SPARC (big-endian) architecture.

#include <stdio.h>

union u_double
{
    double  dbl;
    char    data[sizeof(double)];
};

union u_float
{
    float   flt;
    char    data[sizeof(float)];
};

static void dump_float(union u_float f)
{
    int exp;
    long mant;

    printf("32-bit float: sign: %d, ", (f.data[0] & 0x80) >> 7);
    exp = ((f.data[0] & 0x7F) << 1) | ((f.data[1] & 0x80) >> 7);
    printf("expt: %4d (unbiassed %5d), ", exp, exp - 127);
    mant = ((((f.data[1] & 0x7F) << 8) | (f.data[2] & 0xFF)) << 8) | (f.data[3] & 0xFF);
    printf("mant: %16ld (0x%06lX)\n", mant, mant);
}

static void dump_double(union u_double d)
{
    int exp;
    long long mant;

    printf("64-bit float: sign: %d, ", (d.data[0] & 0x80) >> 7);
    exp = ((d.data[0] & 0x7F) << 4) | ((d.data[1] & 0xF0) >> 4);
    printf("expt: %4d (unbiassed %5d), ", exp, exp - 1023);
    mant = ((((d.data[1] & 0x0F) << 8) | (d.data[2] & 0xFF)) << 8) | (d.data[3] & 0xFF);
    mant = (mant << 32) | ((((((d.data[4] & 0xFF) << 8) | (d.data[5] & 0xFF)) << 8) | (d.data[6] & 0xFF)) << 8) | (d.data[7] & 0xFF);
    printf("mant: %16lld (0x%013llX)\n", mant, mant);
}

static void print_value(double v)
{
    union u_double d;
    union u_float  f;

    f.flt = v;
    d.dbl = v;

    printf("SPARC: float/double of %g\n", v);
//    image_print(stdout, 0, f.data, sizeof(f.data));
//    image_print(stdout, 0, d.data, sizeof(d.data));
    dump_float(f);
    dump_double(d);
}


int main(void)
{
    print_value(+1.0);
    print_value(+2.0);
    print_value(+3.0);
    print_value( 0.0);
    print_value(-3.0);
    print_value(+3.1415926535897932);
    print_value(+1e126);
    return(0);
}

The commented out 'image_print()` function prints an arbitrary set of bytes in hex, with various minor tweaks. Contact me if you want the code (see my profile).

If you're using Intel (little-endian), you'll probably need to tweak the code to deal with the reverse bit order. But it shows how you can do it - using a union.

like image 56
Jonathan Leffler Avatar answered Sep 28 '22 07:09

Jonathan Leffler


You cannot directly apply bitwise operators to float or double, but you can still access the bits indirectly by putting the variable in a union with a character array of the appropriate size, then reading the bits from those characters. For example:

string BitsFromDouble(double value) {
    union {
        double doubleValue;
        char   asChars[sizeof(double)];
    };

    doubleValue = value; // Write to the union

    /* Extract the bits. */
    string result;
    for (size i = 0; i < sizeof(double); ++i)
        result += CharToBits(asChars[i]);
    return result;
}

You may need to adjust your routine to work on chars, which usually don't range up to 4096, and there may also be some weirdness with endianness here, but the basic idea should work. It won't be cross-platform compatible, since machines use different endianness and representations of doubles, so be careful how you use this.

like image 21
templatetypedef Avatar answered Sep 28 '22 09:09

templatetypedef


Bitwise operators don't generally work with "binary representation" (also called object representation) of any type. Bitwise operators work with value representation of the type, which is generally different from object representation. That applies to int as well as to double.

If you really want to get to the internal binary representation of an object of any type, as you stated in your question, you need to reinterpret the object of that type as an array of unsigned char objects and then use the bitwise operators on these unsigned chars

For example

double d = 12.34;
const unsigned char *c = reinterpret_cast<unsigned char *>(&d);

Now by accessing elements c[0] through c[sizeof(double) - 1] you will see the internal representation of type double. You can use bitwise operations on these unsigned char values, if you want to.

Note, again, that in general case in order to access internal representation of type int you have to do the same thing. It generally applies to any type other than char types.

like image 29
AnT Avatar answered Sep 28 '22 07:09

AnT


Do a bit-wise cast of a pointer to the double to long long * and dereference. Example:

inline double bit_and_d(double* d, long long mask) {
  long long t = (*(long long*)d) & mask;
  return *(double*)&t;
}

Edit: This is almost certainly going to run afoul of gcc's enforcement of strict aliasing. Use one of the various workarounds for that. (memcpy, unions, __attribute__((__may_alias__)), etc)

like image 38
KitsuneYMG Avatar answered Sep 28 '22 09:09

KitsuneYMG