This is basically a repost of http://www.gamedev.ru/flame/forum/?id=228402 .
As many programmers know, there is quite a lot of undefined behavior in C++ shifts.
So let's make our own ones!
Since the task is relatively small, it seems worth doing WELL, to save everyone using it a bit of time.
Motivation:
0. Well-defined semantics rulez.
1. For many cases (e. g. shifts by a constant) the result should be exactly the same as built-in shifts, with no performance penalty.
2. For many other cases performance does not matter.
3. If old code (with built-in shifts) worked, then, presumably the shift amount was always in the allowed range, and the branch in the new code would have essentially 100% prediction.
4. For the cases, where shifts are variable, and performance matters so much, that even predicted branches make difference (e. g. coding/decoding) - well there built-in shifts still remain. Hopefully, it is <5% of code.
My present attempt:
// This software is in the public domain. Where that dedication is not
// recognized, you are granted a perpetual, irrevocable license to copy
// and modify this file as you see fit.
template<typename L,typename R>
L shl(L value,R amount)
{
if(amount>=R(sizeof(L))*8) return L(0);
if(amount<R(0))
{
if(amount<=-R(sizeof(L))*8)
{
if(value<L(0)) return L(-1);
return L(0);
}
return value>>(-amount);
}
return value<<amount;
}
template<typename L,typename R>
L shr(L value,R amount)
{
if(amount>=R(sizeof(L))*8)
{
if(value<L(0)) return L(-1);
return L(0);
}
if(amount<R(0))
{
if(amount<=-R(sizeof(L))*8) return L(0);
return value<<(-amount);
}
return value>>amount;
}
I subject it to scrutiny of the community.
Basically, pretty please, check that all corner cases actually work.
Quite a bit of trickery went into the writing this code, to satisfy the rules of C++. As you can imagine, there are A LOT of corner cases, and I am not confident everithing works correctly.
Some useful links:
http://www.gamedev.ru/flame/forum/?id=228402 - original post (in Russian)
http://en.cppreference.com/w/cpp/language/operator_arithmetic - for language-lawering
http://en.cppreference.com/w/cpp/language/implicit_cast#Integral_promotion - likewise
http://rextester.com - for testing snippets
http://gcc.godbolt.org - for testing assembly output
P. S. Does anyone see a nice way to do it in C? Templates are C11, macros evaluate arguments multiple times, and lots of functions are ugly.
I'm asking because it seems like a good candidate for inclusion into stb_* (right along with stb_divide.h), and maybe if we ask really nicely, Sean Barrett can do just that.
↧