4 ms·
is casting to (double*) unsafe in this case and are there platforms where using memcpy would be slower because the compiler doesn't know the data is correctly a
by benmmurphy 4y ago
is casting to (double*) unsafe in this case and are there platforms where using memcpy would be slower because the compiler doesn't know the data is correctly aligned? i see in x86-64 its using movsd which allows for both aligned and unaligned access, with unaligned access just being slower.
- unwind 4y agoCasting should be safe as far as I know, the point of void pointers is the ability to point at any kind of data. Not sure about the platform question, I tried building it for a Cortex-M3 (32-bit "small" ARM) but that still doesn't issue the memcpy() call. Instead it just loads pairs of words from memory then moves those into the FPU for comparison. The loads are marked with "@unaligned" but not sure what that actually means and didn't spend the time to research it now. [1]: https://godbolt.org/z/PGTb8abP8 https://godbolt.org/z/PGTb8abP8
- ksherlock 4y agoSwitching among godbolt compilers (at optimization 0) ... gcc on less common architectures (like esp32, risc32 or msp430), icc, and msvc don't optimize out the memcpy.
- mattarm 4y ago> Casting should be safe as far as I know I assume you're talking about this: long x = <whatever> double y = *(double*)&x; Nope, it is undefined behavior, with few exceptions, even if sizeof double == sizeof long. See https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule https://stackoverflow.com/questions/98650/what-is-the-strict... If you got rid of this rule a huge number of optimizations would be lost, since the compiler would have to assume that any memory, of any type, could change if anything at all is modified through a pointer. You'd be surprised how slow everything would be. You'd also be surprised how much code violates this and gets away with it. Time bombs waiting to happen. ;-) memcpy(), and more generally accessing bytes through char*, are how you avoid this kind of undefined behavior. It comes up so often that C++20 added std::bit_cast for this same use: https://en.cppreference.com/w/cpp/numeric/bit_cast https://en.cppreference.com/w/cpp/numeric/bit_cast
- benmmurphy 4y agoI think he's referring to the cast: (double*)->(void*)->(double*) which should be fine. I'm guessing the original article author prefers using memcpy because there doesn't seem to be any performance cost and its correct in other situations where the alignment is wrong, or there is type aliasing. The advantage would be if you always use a safe method in a code base then you don't have to worry about proving that possibly unsafe methods are safe.
- unwind 4y agoNo, in this context (a qsort() comparison callback) I'm talking about using casts to access the values to compare. Here's how I would have written it, without memcpy(): static int cmp_double(const void *va, const void *vb) { const double a = *(const double *) va, b = *(const double *) vb; return a < b ? -1 : a > b; } I know you can't "jump types", but this doesn't, since qsort() is passing pointers to doubles and we're just getting them back. Edit: missed const:s in function body oops.
- scaredginger 4y agoYes, you're right in that casting an arbitrary void * to double and dereferencing would be unsafe, but in this case, the void pointers point to doubles by construction, so they must be aligned
- mattarm 4y agoCasting from any T* to void* back to T* is always safe. That is one of the few safe things you can do with a void*, and kinda the whole point. :-) I think casting from void* back to double* is the better option here. memcpy() is overkill here since that idiom is mainly for reinterpreting data with a different type without triggering undefined behavior: https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule https://stackoverflow.com/questions/98650/what-is-the-strict.... We're not changing the type, so why confused people with an overly powerful idiom? That said, the memcpy() idiom is common and useful enough that C++ added a specific API for it: https://en.cppreference.com/w/cpp/numeric/bit_cast https://en.cppreference.com/w/cpp/numeric/bit_cast. Casting and the memcpy() idiom compile down to the same code https://godbolt.org/z/xY6W9ja69 https://godbolt.org/z/xY6W9ja69*
- Teckla 4y agoCasting from any T* to void* back to T* is always safe. Are you sure this also applies to function pointers?