The weblog of Nicholas Chapman A proposal for C++: bit_cast. C++ should have a bit_cast function. bit_cast<T>(x) would get the bits of x, and reinterpret them as the bits of a value of type T. For example, you would be able to write float x = 1.0f; const uint32_t y = bit_cast<uint32_t>(x); And then y would have the value 0x3f800000. And then y would have the value 0x3f800000. From a generated-code point of view this would be a no-op. (Or maybe some instructions would have to be emitted for moving to/from floating point registers in this case?) Edit: A bit_cast is only valid if the source and destination types are the same size. Using bit_cast on types differing in size would give a compile-time error. This kind of bit conversion needs to be done pretty often if you are writing low-level, high peformance code, or networking code. There are a number of ways to do something similar with C++ today, but they all have problems, either with correctness or elegance. Current option 1: pointer casting Possibly the most old-school way is to pointer cast, like this: float x = 1.0f; const uint32_t y = *(uint32_t*)&x; The problem here is that it is undefined behaviour to write through one pointer type (float*) and read through another (uint32_t*). (due to aliasing rules). Compilers may whinge about it and/or generate code that does not do what you want. For example, GCC says Possibly the most old-school way is to pointer cast, like this:The problem here is that it is undefined behaviour to write through one pointer type (float*) and read through another (uint32_t*). (due to aliasing rules). Compilers may whinge about it and/or generate code that does not do what you want. For example, GCC says "warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]" (see code example here: https://goo.gl/wZcV2D). Current option 2: union abusing The trick here is to write into one of the union fields, then read from the other one. float x = 1.0f; union fi_union { float f; uint32_t i; }; fi_union u; u.f = x; const uint32_t y = u.i; This *may* work in practice, but is syntactically ugly, is an abuse of the idea of unions, and may be undefined behaviour. Current option 3: memcpy The idea here is to just memcpy() from the src to the dest, and rely on the compiler to recognise what you are doing, and to not actually emit the memcpy function call. Example: template <class Src, class Dest> inline Dest bit_cast(const Src& x) { static_assert(sizeof(Src) == sizeof(Dest), "sizeof(Src) == sizeof(Dest)"); Dest d; std::memcpy(&d, &x, sizeof(Dest)); return d; } This works well in practice, but is a little dangerous in the sense that you are relying on a compiler optimisation to avoid very slow code (e.g. it would be very slow if a function call was actually emitted). Current (non-)option 4: static_cast static_cast does not do what we want. float x = 1.0f; const uint32_t y = static_cast<uint32_t>(x); puts the value '1' in y, not 0x3f800000 as we want. Current (non-)option 5: reinterpret_cast To be honest I'm not sure what reinterpret_cast is for. I never use it. It also doesn't do what we want: float x = 1.0f; const uint32_t y = reinterpret_cast<uint32_t>(x); gives, in Visual Studio 2012: "error C2440: 'reinterpret_cast' : cannot convert from 'float' to 'uint32_t' Conversion is a valid standard conversion, which can be performed implicitly or by use of static_cast, C-style cast or function-style cast" Semantics (Added in an edit) The trick here is to write into one of the union fields, then read from the other one.This *may* work in practice, but is syntactically ugly, is an abuse of the idea of unions, and may be undefined behaviour.The idea here is to just memcpy() from the src to the dest, and rely on the compiler to recognise what you are doing, and to not actually emit the memcpy function call. Example:This works well in practice, but is a little dangerous in the sense that you are relying on a compiler optimisation to avoid very slow code (e.g. it would be very slow if a function call was actually emitted).static_cast does not do what we want.puts the value '1' in y, not 0x3f800000 as we want.To be honest I'm not sure what reinterpret_cast is for. I never use it. It also doesn't do what we want:gives, in Visual Studio 2012: "error C2440: 'reinterpret_cast' : cannot convert from 'float' to 'uint32_t' Conversion is a valid standard conversion, which can be performed implicitly or by use of static_cast, C-style cast or function-style cast"(Added in an edit) The bit_cast would give the same result as the bit_cast function above: template <class Src, class Dest> inline Dest bit_cast(const Src& x) { static_assert(sizeof(Src) == sizeof(Dest), "sizeof(Src) == sizeof(Dest)"); Dest d; std::memcpy(&d, &x, sizeof(Dest)); return d; } Because we know 'd' (the destination) is in a different memory location from 'x' (the source), there shouldn't be any aliasing problems. Conclusion So in conclusion, the alternatives are basically hacky workarounds to something missing in the original language. Older alternatives are increasingly rendered invalid due to fussier optimising compilers. I think the problem should be solved once and for all with a built-in language feature - bit_cast. Because we know 'd' (the destination) is in a different memory location from 'x' (the source), there shouldn't be any aliasing problems.So in conclusion, the alternatives are basically hacky workarounds to something missing in the original language. Older alternatives are increasingly rendered invalid due to fussier optimising compilers. I think the problem should be solved once and for all with a built-in language feature - bit_cast. Edit: reddit discussion here. Edit: posted to 'ISO C++ Standard - Future Proposals' list here.

Do you have a comment or feedback about this blog post? Please email me. < Back