On 07/14/2009 10:44 PM, Andrew Troschinetz wrote:
Hi all, I'm having an argument about byteswapping floating point types and since the argument is mainly concerning the behavior of the compiler (and in this case of GCC specifically) I figure this list is a good place to ask for information. A co-worker has preposed this function (simplified here) for providing byteswaping for both integral and floating point types. template<class T> T byteswap(T value) { T result; size_t n = sizeof (T); unsigned char* p = (unsigned char*) &value; for (size_t i = 0; i < n; ++i) ((unsigned char*) &result)[i] = p[n - 1 - i]; return result; } Now, aside from the fact that this template is _too_ generic and accepts things it really shouldn't, it does successfully byteswap both integral and floating point types. I believe that manipulating the underlying bytes of floating point values via access through unsigned char* should be safe, as opposed to a methods of byteswaping by leveraging unions which breaks aliasing rules.
Doing this with unions doesn't break aliasing rules on gcc.
But for floating point types doesn't this signature run the risk of the compiler putting a byteswapped floating point value into an FPU register, thus causing the data to be modified?
Why would it? Perhaps you're concerned that loading into a FPU register would convert a signaling NaN into a quiet NaN, or somesuch?
Testing with GCC 3.5.x to GCC 4.x has shown that this issue doesn't surface unless you attempt some mathematical operation on the float containing a byteswaped value. In other words: // This works float before = 50.125484; float swapped = byteswap (before); float after = byteswap (swapped); assert (before == after); // This fails float before = 50.125484; float swapped = byteswap (before); swapped += 0; float after = byteswap (swapped); assert (before == after); My concern is that the compiler is free to put floating point values into FPU registers as it sees fit and relying on the compiler not doing so is dangerous. He feels that my concern is misguided and if his implementation of byteswap() doesn't work it's because the compiler is broken or someone is trying to do something with byteswapped floating point values that they shouldn't be doing.
You're in the realm of at best implementation-defined or at worst undefined behaviour. The CPUs I know are quite happy to do exact copies of memory via floating-point registers, and some versions of memcpy() make use of that. But in general it's not guaranteed. Andrew.