5

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 would 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?

Community
  • 1
  • 1
eold
  • 5,972
  • 11
  • 56
  • 75
  • 2
    Do you run into a lot of compilers that get this wrong? Do us a favor and document their names. – Hans Passant Feb 25 '11 at 22:29
  • I have found none so far, but I did not searhed for any. If I find any I will create a list here. – eold Feb 25 '11 at 22:33
  • 1
    I think it's pretty much a non-issue, just like ones complement, sign/magnitude, padding bits, EBCDIC, and a lot of other ridiculous things the standard allows. – R.. GitHub STOP HELPING ICE Feb 25 '11 at 23:48
  • 1
    any smart compiler will determine the result of the expression at compile-time and optimize out the test and the branch that is unused. Don't worry about it. – Matthieu M. Feb 26 '11 at 11:13
  • @HansPassant what do you mean by "get this wrong"? The behaviour of this shift is *implementation-defined*. – M.M Jul 24 '15 at 00:39

7 Answers7

8

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.

thkala
  • 84,049
  • 23
  • 157
  • 201
  • What do you mean by platform? Does the behaviour of right shift depend on particular architecture, operating system, or both? – eold Feb 25 '11 at 22:26
  • @leden: I just provided a generic approach for all those cases where OS/CPU may vary. I do not know out of hand if the shift operation in C can vary among different CPUs, although I don't think it would vary among compilers or OSes on the same CPU. – thkala Feb 25 '11 at 22:32
  • I disagree, and I'll try to do so as respectfully as I can. GNU autotools have a number of problems in and of themselves which I'll try not to rant about here, but perhaps the worst aspect of the way they're typically used is doing completely backwards tests for things that can be checked equally-or-more-easily at the source level using either the preprocessor or simple conditionals on constant expressions. OP's question is definitely such a case. – R.. GitHub STOP HELPING ICE Feb 26 '11 at 00:21
  • If leden is actually running on this very odd hardware, what are the odds that GNU autotools are available there?? – Bo Persson Feb 26 '11 at 10:09
7

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

#if ((-1)>>1) == (-1))
...
#else
...
#endif
Eclipse
  • 44,851
  • 20
  • 112
  • 171
Giuseppe Guerrini
  • 4,274
  • 17
  • 32
  • You could `#define IS_ARITHMETIC_RS (-2 >> 1 == -1)` and then litter your code with `#if IS_ARITHMETIC_RS ... #else` – Marlon Feb 25 '11 at 22:38
  • This is what I have been looking for. Are there any possible problems with this approach? – eold Feb 25 '11 at 22:38
  • 4
    Is the preprocessor guaranteed to give exactly the same result as the compiler? I highly doubt it, especially when cross-compiling. – marton78 Sep 14 '12 at 07:45
  • 1
    It "should", also in a cross-compilation. But your doubt is justified, since the preprocessor is often a separate tool weakly coupled to the compiler. A more secure way is to use the same conditional expression as pure C ("if (...) { ..." instead of "#if ...") and let the compiler detect that one of the two branches won't never be executed since the test expression is constant. The majority of compilers don't generate code. Many also generate a warning... Some compilers do generate useless code :( – Giuseppe Guerrini Sep 14 '12 at 08:45
  • ...of course, the solution without preprocessor works only inside a function (while the preprocessor can be applied also to definitions, initializers, includes...), and both the branches must be syntactically correct. – Giuseppe Guerrini Sep 14 '12 at 09:01
5

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.

user1174627
  • 57
  • 1
  • 4
3

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>();
}
Mark B
  • 95,107
  • 10
  • 109
  • 188
  • The division is problematic in my case because it rounds towards zero, but I actually need rounding down. Btw, very nice solution with the templates, I wonder what are the pros/cons of this one over the standard #if ? – eold Feb 25 '11 at 22:44
  • @leden It basically changes the division of the logic into two template functions rather than using the preprocessor to put the right code in each case using `#if`. – Mark B Feb 25 '11 at 22:54
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.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
1

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
Community
  • 1
  • 1
Christoph
  • 164,997
  • 36
  • 182
  • 240
  • @imallett: it's been a few years, but I think you're right - the closing parens should go before the `==` – Christoph Jul 23 '15 at 09:15
  • I think it's still incorrect. `~-2` is `1`, so the `>>` result is `0` no matter what. Also, your macro will not work for positive numbers. – geometrian Jul 23 '15 at 22:13
  • @imallett: the condition should be fine as-is `~(~(-2) >> 1) == ~(1 >> 1) == ~0 == -1` – Christoph Jul 24 '15 at 00:34
  • the point is that it is _always_ true, regardless of the shift type. I see the shift has been fixed for simple arguments :) – geometrian Jul 24 '15 at 01:02
  • @imallett: *the point is that it is always true, regardless of the shift type* - that's why it comes second; it's a sanity check (eg sign-magnitude representations should fail it) – Christoph Jul 24 '15 at 09:29
0

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.

Community
  • 1
  • 1
marton78
  • 3,899
  • 2
  • 27
  • 38