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

后端 未结 6 1543
遇见更好的自我
遇见更好的自我 2020-12-17 22:16

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 \"

6条回答
  •  悲&欢浪女
    2020-12-17 23:13

    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 
    
    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.

提交回复
热议问题