开发者

Compile-time checking if right shift is arithmetic on signed types

I am wondering what is the most portable way of checking whether right shift is arithmetic when operating for on signed types (e.g. whether -2 >> 1 is -1) at compile-time.

My idea is to check this somehow on compile time and be able to detect this, so I can compile different versions of the function (depending whether the operator >> is really arithmetic shift or not).

By reading the topic Verifying that C / C++ signed right shift is arithmetic for a particular compiler? I came to the idea to initialize a flag

static const bool is_arithmetic_rs = (((signed int)-1)>>1) == ((signed int)-1));

and to test it at run-time like this:

if (is_arithmetic_rs) {
  // some fast algorithm using arithmetic right shifts (using >> operator)
} else {
  // the same algorithm without arithmetic right shifts (much slower)
}

However, I would like to avoid this branching if possible every time. For simplicity, let's suppose I want to implement a portable arithmetic right shift; if I had to check this every time a function is called, this w开发者_运维问答ould be a huge performance impact, so I would like to do it at compile time, if possible.

If there exist no portable way of doing this check, is there a way to do this by checking on the best-effort basis, like checking with ifdefs for a specific compiler/platform?


The best way to perform such checks is what e.g. GNU autotools do:

  • Compile a small program on your target platform and test what happens

  • Set an appropriate #define in a header file

  • Include that header file in your source files

  • Optionally, use appropriately defined macros so that you don't clutter your code with #ifdef directives for every little thing.

  • Compile your main project

That way you avoid having to create tables with the supported features and the various quirks of each hardware platform and operating system in the wild - let alone their combinations. If you do not build your code on your target, however, you will have to replace the first step with a pre-supplied table/list of features for your target.

You should probably have a look at widely used build systems such as GNU autotools or CMake, in order to reuse existing macros and platform-specific information and avoid having to create your own and therefore reinvent the wheel.

BTW, any decent compiler these days should optimise-out simple tests with constant expressions, so using a runtime test where necessary - perhaps through a macro - should not hurt performance too much. You should test and profile your code to find out.


The branch could be avoided by using a preprocessing-time test

#if ((-1)>>1) == (-1))
...
#else
...
#endif


Really more of a comment than answer (but apparently I'm not reputable)

A couple of the answers here use pre-processor checks like

#if ((-1)>>1) == (-1))

Personally, I wouldn't trust the pre-processor to tell me what kind of code the compiler generates.


Have you actually verified that your compiler doesn't optimize division into arithmetic shift when it's available?

Otherwise I think you can use templates.

template <bool B>
void do_work();

template <>
void do_work<true>()
{
    // Do stuff with H/W.
}

template <>
void do_work<false>()
{
    // Do slow stuff with S/W.
}

do_work<(-2 >> 1) == -1>();

Then make it prettier to use with an inline function:

inline real_do_work()
{
    do_work<(-2 >> 1) == -1>();
}


Try this:

#define SAR(x,y) ((x)>=0) ? ((x)>>(y)) : (~(~(x)>>(y)))

A good compiler will optimize this to ((x)>>(y)) assuming the CPU is sane.

Feedback is welcome as to which compilers are good.


Inspired Giuseppe's and R..'s answers:

#if -2 >> 1 == -1
#define rshift_ar(X, Y) ((X) >> (Y))
#elif ~(~(-2) >> 1) == -1
#define rshift_ar(X, Y) ((X) >= 0 ? (X) >> (Y) : ~(~(X) >> (Y)))
#else
#error "unsupported shifting semantics"
#endif


All this preprocessor magic is useless with any decent compiler. Did you verify that your code given above really generates a branch in its output? I highly doubt it, since the static const bool can an will be evaluated as a compile time constant, so the false-part of your if (is_arithmetic_rs) will be eliminated by the compiler, with anything above -O1. See also my answer here.

Besides, I doubt that the preprocessor's output is guaranteed to be the same as the compiler's, especially when cross-compiling between platforms that have different shifts.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜