5 ms·
Would a union do the trick?
by IAmLiterallyAB 6y ago
Would a union do the trick?
- qppo 6y agoThat's the Linux way to do it union { int i; float f } u { .f = 1.23f }; int i = u.i; Another way is a memcpy, which I believe is the most defined way to do type punning int i; float f; memcpy(&i, &f, 4); But you also have to assume the size of those primitive types but that's pretty safe in modern C/C++.
- tsomctl 6y agoDo modern compilers optimize away the memcpy?
- qppo 6y agoyes
- colejohnson66 6y agoIn C++, type punning through pointers and unions is undefined behavior. Even `reinterpret_cast<T>` isn’t allowed because of aliasing (IIRC). The only “defined” way to do type punning is a memcpy. A compiler targeting something like x86 would optimize out the memcpy. For more information, see the C++20 final draft[0§7.6.1.9] [0]: https://isocpp.org/files/papers/N4860.pdf https://isocpp.org/files/papers/N4860.pdf
- qppo 6y agoThere's a Torvalds rant about this specific UB and it's how they do it in the Linux kernel.
- sharpneli 6y agoLinux is written in C. In C99 and later type punning trough unions is well defined operation. No issues there. It’s C++ that’s lacking this feature. Not C.
- TwoBit 6y agoI really wish C and C++ had an explicit type punning operator. They are systems programming languages and this is very often needed.
- Kranar 6y agoC++20 introduces bit_cast to do this. The implementation of bit_cast is semantically equivalent to using memcpy.
- enriquto 6y agoCasting pointers in C used to work back in the day. It still works in most cases (except with aggressive optimization options), but it may may make some people uneasy.
- andi999 6y agoI always thought the memcopy is optimized out, but can you explain how in int i; float f; memcopy (&i, &f,4) the memcopy can be optimized out? Probably I am misunderstanding the statement.
- hurrrrrrrr 6y agoThe compiler is not beholden to the standard library but the standard. So all modern compilers come with a bit of knowledge of how standard library functions like memcpy are supposed to behave and as long as the visible effect is the same it's allowed to do anything it wants. So instead of calling the function memcpy for a size of 4, it can e.g. just use a mov instruction to move the value from a float to an integer register. [1] It does additional analysis on how the values are used and maybe it doesn't even need to move the value at all, but that's the gist of it. [1] https://gcc.godbolt.org/z/x4P7jE https://gcc.godbolt.org/z/x4P7jE
- colejohnson66 6y agoA good example of this is the string functions in the C stdlib. I’ve reverse engineered some programs where the compiler used x86’s built-in “string” instructions instead of calling out to, say, `strlen`.
- andrewaylett 6y agoThe C abstract machine presents i and f as having separate locations in memory, but a compiler that knows that memcopy does can avoid actually saving the target machine registers to the stack, then avoid even copying values between registers.
- deleted 6y ago[deleted]
- ChrisFoster 6y agoPunning through the union is explicitly allowed in the GCC documentation which explains why it's reliable for use in the Linux kernel. See https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Type%2Dpunning https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Typ... > you also have to assume the size of those primitive types This usually works though there's some situations where you could still be surprised. For instance if you're programming embedded processors with avr-gcc you'll have sizeof(double)==4
- SoSoRoCoCo 6y agoYes, that was the only "portable solution". I use quotes because we had to assert() the sizeof float with uint32_t in the code prologue (it is unlikely to be an issue now, but the code is 25 years old, so we stuck to the spirit of expecting compilers where C types could be a variety of sizes, not just the "conventional" sizes of today). Unions are defined, casting is very poorly defined, especially float<>int. There's a good link a few comments down.