开发者

Verifying that C / C++ signed right shift is arithmetic for a particular compiler?

开发者 https://www.devze.com 2022-12-09 04:15 出处:网络
According to the C / C++ standard (see this link), the >> ope开发者_开发问答rator in C and C++ is not necessarily an arithmetic shift for signed numbers.It is up to the compiler implementation whether

According to the C / C++ standard (see this link), the >> ope开发者_开发问答rator 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.");


From your various comments, you talk about using this cross-platform. Make sure that your compilers guarantee that when they compile for a platform, their compile-time operators will behave the same as run-time ones.

An example of differing behavior can be found with floating point numbers. Is your compiler doing its constant-expression math in single, double, or extended precision if you're casting back to int? Such as

constexpr int a = 41;
constexpr int b = (a / 7.5);

What I am saying is, you should make sure your compilers guarantee the same behavior during run-time as compile-time when you're working across so many different architectures.

It is entirely possible that a compiler might sign-extend internally but not generate the intended opcode(s) on the target. The only way to be sure is to test at run-time or look at the assembly output.

It's not the end of the world to look at assembly output...How many different platforms are there? Since this is so performance-critical just do the "work" of looking at 1-3 lines of assembler output for 5 different architectures. It isn't as if you have to dive through an entire assembly output (usually!) to find your line. It's very, very easy to do.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号