How to use bitwise operators in "double" in C ++?

I was asked to get the internal binary representation of different types in C. Currently, my program works fine with 'int', but I would like to use it with "double" and "float". My code is as follows:

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 does not work when I try to instantiate a template using "double" or "float".

+9
source share
6 answers

In short, no.

Bit operators do not make sense with double or float , and the standard says that bit operators ( ~ , & , | , ^ , >> , << ) and assignment options) do not accept double or float operands.

Both double and float have 3 sections - sign bit, exponent and mantissa. Suppose for a moment that you could shift double to the right. The exponent, in particular, means that there is no simple translation to shift the bit pattern to the right - the sign bit will move to the exponent, and the least significant bit of the exponent will move to the mantissa with completely non-obvious sets of values. IEEE 754 implies 1 bit before the actual mantissa bits, which also complicates the interpretation.

Similar comments apply to any of the other bitwise operators.

Thus, since there is no robust or useful interpretation of bitwise operators for double values, they are not allowed by the standard.


From the comments:

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

This code was written several years ago for the 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 function 'image_print ()' prints a random set of bytes in hexadecimal with various minor changes. Contact me if you want to receive a code (see my profile).

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

+16
source

You cannot directly apply bitwise operators to a float or double , but you can access the bits indirectly by placing the variable in union with an array of characters of the appropriate size, and then reading the bits from these characters. For instance:

 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 set up your routine to work with characters that usually do not reach 4096, and there may be some kind of weirdness with enthusiasm, but the basic idea should work. This will not be cross-platform compatibility, as the machines use different continents and representations of doubles, so be careful how you use it.

+10
source

Bitwise operators usually do not work with the binary representation (also called object representation) of any type. Bitwise operators work with a type representation value, which is usually different from an object representation. This applies to int as well as to double .

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

for instance

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

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

Note that in general, to access an internal representation of type int you need to do the same. It usually applies to any type other than char types.

+4
source

Do a bit-wise translation of the pointer from double to long long * and look for it. Example:

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

Edit: This will almost certainly be launched by gcc forcing the strictest aliasing. To do this, use one of the workarounds. ( memcpy , unions, __attribute__((__may_alias__)) , etc.)

+3
source

Another solution is to get a pointer to a floating point variable and pass it to a pointer to an integer type of the same size, and then get the value of the integer that this pointer points to. Now you have an integer variable with the same binary representation as a floating point, and you can use your bitwise operator.

 string findBin(float f) { string binary; for(long i = 4096 ; i >= 1; i/=2) { long x = * ( long * ) &y; if((x & i) != 0) binary += "1"; else binary += "0"; } return binary; } 

But remember: you must throw the type with the same size. Otherwise, unpredictable events (for example, buffer overflow, access violation, etc.) may occur.

+1
source

As others have said, you can use the bitwise operator on a double, casting double* to long long* (or sometimes just long* ).

 int main(){ double * x = (double*)malloc(sizeof(double)); *x = -5.12345; printf("%f\n", *x); *((long*)x) &= 0x7FFFFFFFFFFFFFFF; printf("%f\n", *x); return 0; } 

This code prints on my computer:

 -5.123450 5.123450 
0
source

Source: https://habr.com/ru/post/1336247/


All Articles