0

Let me start by saying that I understand that performing bit shifts and other bit-wise operations on a floating-point value sounds meaningless and ill-advised. So, moving beyond the "why would you do such a thing" responses...

I have a templated function that is handling some portion of serializing and deserializing various types to/from a binary stream. To "reassemble" multi-byte types from the byte stream, I am making use of bitshifts and bitwise ORs in the usual way. Please assume also that I have endianness and other such concerns under control.

Cutting to it, I'm looking for a clean way within a templated function to reliably cast a variable to an integer of the same size as the argument type (so that I'm allowed to perform bitwise operations on it); where that argument type might be a floating-point or any other arbitrary non-integer type. Pseudo-code example:

uint_same_size_as(U) sameSizeInt;

Where uint_same_size_as(U) would evaluate to, say, uint32_t when U is of type float, or uint64_t when U is of type double, etc.

Is there any such animal? And please forgive me if this is a basic feature of templates that I'm ignorant of.

Matthew M.
  • 392
  • 2
  • 11
  • Why do you need bit-shift at all? Work on byte level – Slava Jan 27 '17 at 17:57
  • To put it simply, I have reason to want to work on the bit level ... say I have a 32-bit type with more precision than I need but 24-bits is not enough ... I want the flexibility to serialize/deserialize a sequence of values of arbitrary bit-length. – Matthew M. Jan 27 '17 at 18:04
  • Then use `unsigned char *` copy enough bytes and do shift on last byte if necessary (which I doubt would happen). And I doubt it will work correct for floating point data. – Slava Jan 27 '17 at 18:06

1 Answers1

1

Due to the strict aliasing rule even if you would realize the way that code would lead to UB. Better is to cast address of variable to [unsigned] char * and copy there in forward or reverse direction based on endianess. It is not clear why you need convoluted way with bit shifts to achive that.

Though you can use std::bitset<sizeof<T>*CHAR_BIT> in your template, but I strongly doubt that simply copying and shifting bits will work for floating types of different size.

Community
  • 1
  • 1
Slava
  • 43,454
  • 1
  • 47
  • 90
  • Bit shifts are required because I am working with the bit as the smallest unit and not the byte. So, if I have a 22-bit int, an 11-bit int, a 32-bit float, and a 7-bit int serialized into my binary stream without padding (e.g., byte alignment) in between then I'll obviously need bitshifts and bitmasks. – Matthew M. Jan 27 '17 at 18:13
  • The only underlying types that will be of arbitrary length will be actual integers. I obviously won't be attempting to transmit or reconstruct a float or double of anything but 32-bit/64-bit. The issue is that the 32-bits constituting a float may not end up byte-aligned in the stream. – Matthew M. Jan 27 '17 at 18:16
  • 1
    @MatthewM. then implement packing/unpacking in `unsigned int` and do that in loop if underlying type is bigger. Bitwise operations should work fastest on int. – Slava Jan 27 '17 at 18:18
  • 1
    @Matthew M.: A useful old trick is to only bit-twiddle the odd-sized data, refilling a bit-buffer shift register from the stream whenever it runs dry and fetching raw bytes as-is for floats and other aligned data. Conversely the serializer reserves a spot for the bit-buffer at the emission of the first bit and then patches in the completed buffer farther back in the stream once full. – doynax Jan 27 '17 at 18:20