Check compilation time if right shift arithmetic shift to signed types

I am wondering what is the most portable way to check if the correct shift is arithmetic when working for signed types (e.g. -2 >> 1 is -1 ) at compile time.

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

After reading the topic Checking that the right shift / C / C ++ shift is arithmetic for a particular compiler? I came up with the idea of ​​initializing the flag

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

and test it at runtime as follows:

 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, suppose I want to implement a portable arithmetic shift to the right; if I had to check this every time a function is called, it will have a huge impact on performance, so I would like to do this at compile time, if possible.

If there is no portable way to do this check, is there a way to do this by checking the best results, for example, checking with ifdefs for a specific compiler / platform?

+4
source share
7 answers

You could avoid branching using the preprocessing test.

 #if ((-1)>>1) == (-1)) ... #else ... #endif 
+7
source

The best way to perform such checks is that, for example, GNU autotools :

  • Compile a small program on the target platform and see what happens

  • Set the appropriate #define in the header file

  • Include this header file in source files

  • Use appropriate macros if necessary so that you don't clutter your code with #ifdef directives for every little #ifdef .

  • Compile your main project

Thus, you avoid creating tables with supported functions and various quirks of each hardware platform and operating system in the wild, not to mention their combinations. However, if you do not create your code for your purpose, you will have to replace the first step with a predefined table / list of functions for your purpose.

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

BTW, any worthy compiler these days should optimize simple tests with constant expressions, so using a run-time test if necessary - perhaps through a macro - should not greatly degrade performance. You must check and verify your code to find out.

+8
source

Have you really confirmed that your compiler does not optimize division by arithmetic shift when it is 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 more attractive to use with the built-in function:

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

Actually more comments than answers (but apparently I don't respect)

Several answers here use checks before the processor, for example

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

Personally, I would not trust the preprocessor to tell me what code the compiler generates.

+3
source

Try the following:

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

A good compiler optimizes this value to ((x)>>(y)) , assuming the processor is normal.

Feedback is appreciated how good compilers are.

+1
source

Inspired by the answers of Giuseppe and R .. :

 #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 
+1
source

All this preprocessing magic is useless to any worthy compiler. Have you confirmed that the above code really generates a branch in its output? I highly doubt it, since the static const bool can 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 .

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

0
source

Source: https://habr.com/ru/post/1341375/


All Articles