According to the C / C++ standard (see this link), the >> operator in C and C++ is not necessarily an arithmetic shift for signed numbers. It is up to the compiler implementation whether 0's (logical) or the sign bit (arithmetic) are shifted in as bits are shifted to the right.
Will this code function to ASSERT (fail) at compile time for compilers that implement a logical right shift for signed integers ?
#define COMPILE_TIME_ASSERT(EXP) \
typedef int CompileTimeAssertType##__LINE__[(EXP) ? 1 : -1]
#define RIGHT_SHIFT_IS_ARITHMETIC \
( (((signed int)-1)>>1) == ((signed int)-1) )
// SHR must be arithmetic to use this code
COMPILE_TIME_ASSERT( RIGHT_SHIFT_IS_ARITHMETIC );
Looks good to me! You can also set the compiler to emit an assembly file (or load the compiled program in the debugger) and look at which opcode it emits for signed int i; i >> 1;
, but that's not automatic like your solution.
If you ever find a compiler that does not implement arithmetic right shift of a signed number, I'd like to hear about it.
Why assert? If your compiler's shift operator doesn't suit your needs, you could gracefully remedy the situation by sign-extending the result. Also, sometimes run-time is good enough. After all, the compiler's optimizer can make compile-time out of run-time:
template <typename Number>
inline Number shift_logical_right(Number value, size_t bits)
{
static const bool shift_is_arithmetic = (Number(-1) >> 1) == Number(-1);
const bool negative = value < 0;
value >>= bits;
if (!shift_is_arithmetic && negative) // sign extend
value |= -(Number(1) << (sizeof(Number) * 8 - bits));
}
The static const bool
can be evaluated at compile time, so if shift_is_arithmetic
is guaranteed to be true
, every compiler worth its salt will eliminate the whole if
clause and the construction of const bool negative
as dead code.
Note: code is adapted from Mono's encode_sleb128
function: here.
Update
If you really want to abort compilation on machines without arithmetic shift, you're still better off not relying on the preprocessor. You can use static_assert
(or BOOST_STATIC_ASSERT
):
static_assert((Number(-1) >> 1) == Number(-1), "Arithmetic shift unsupported.");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With