int s = -((unsigned) x >> 31);
int sar = (s^x) >> n ^ s;
This requires 5 bitwise operations.
Explanation
As already mentioned, an arithmetic right shift x >> n
corresponds to the division x / 2**n
. In case the system supports only logical right shift, a negative number can be first converted into a positive number and then its sign is copied back afterwards sgn(x) * (abs(x)/2**n)
. This is equivalent to multiply with +/-1 before and after the right shift sgn(x) * ((sgn(x)*x)/2**n)
.
Multiplying an integer with +/-1 can be emulated with the conditional branchless negation s^(s+x)
or (x^s)-s
. When s
is 0
, nothing happens and x
remains unchanged, so a multiplication with 1. When s
is -1
, we obtain -x
, so a multiplication with -1
.
The first line of the snippet, -((unsigned) x >> 31)
, extracts the sign bit.
Here, the unsigned
conversion ensures compilation into a logical right shift (SHR in assembly). Therefore, the immediate result is 0 or 1, and after the negation s
is 0
or -1
as desired.
With two branchless negations before and after the shift, we arrive at ((s^s+x) >> n) + s ^ s
. This performs a division with rounding the result towards zero (e.g. -5>>1 = -2
). However, an arithmetic right shift (SAR in assembly) floors the result (i.e. -5>>1 = -3
). To achieve this behaviour, one has to drop the +s
operation.
A demo is here: https://godbolt.org/ and https://onlinegdb.com/Hymres0y8.
PS: I arrived here, because gnuplot has only logical shifts.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…