8

Many lossless algorithms in signal processing require an evaluation of the expression of the form ⌊ a / 2b ⌋, where a, b are signed (a possibly negative, b non-negative) integers and ⌊·⌋ is the floor function. This usually leads to the following implementation.

int floor_div_pow2(int numerator, int log2_denominator)
{
    return numerator >> log2_denominator;
}

Unfortunately, the C standard states that the result of the >> operator is implementation-defined if the left operand has a signed type and a negative value.

To ensure the correct behaviour on all platforms, one could replace this simple function with multiple if-else conditions, resulting in poor program performance. (One must to treat an overflow of an integer and consider the case when the numerator is INT_MIN.)

Therefore I ask, what is the best practice for implementation of the arithmetic right shift in C? Ideally, I'm looking for the construct that compiles into the same code1 as the code fragment above while avoiding the implementation-defined behaviour.

1 considering, e.g., gcc and x86-64 platform

UPDATE:

After some thought, I realized that I made an incorrect implication in the above question. Computing the floor function for negative numbers using an arithmetic shift doesn't make sense if the platform doesn't use two's complement. The goal is to implement the expression ⌊ a / 2b ⌋ in a portable way.

DaBler
  • 2,695
  • 2
  • 26
  • 46
  • 5
    Implementation defined, means that it is defined, but not portable (other implementations may do it differently). (GCC tries do define undefined behaviour as well.) For gcc on x86 it is well defined. You can use the pre-processor, to enable a slower implementation, when needed. – ctrl-alt-delor Dec 12 '18 at 16:30
  • 1
    What is the needed range of `log2_denominator`? e.g. 32-bit `int`: Do you need [0..31], [0..30], [1..31], [1..30], or what? – chux - Reinstate Monica Dec 12 '18 at 17:11
  • Have you tried `numerator / (1 << log2_denominator);` with high optimization active? – chux - Reinstate Monica Dec 12 '18 at 17:15
  • 1
    @chux In practice, the _b_ is a small compile-time constant. So we can assume that _b_ is in [0..`sizeof(int)`-1]. – DaBler Dec 12 '18 at 17:29
  • 1
    @chux In C, the ' / ' operator always rounds towards zero. This is not the floor function. – DaBler Dec 12 '18 at 17:31
  • True, `/` not floor for all `num < 0`. – chux - Reinstate Monica Dec 12 '18 at 17:34
  • Aside: As coding goal uses a **constant** `b`, code `c = floor_div_pow2(a, CONSTANT_B);` is counter productive to program performance. `c = MACRO(a, CONSTANT_B)` makes more sense. – chux - Reinstate Monica Dec 12 '18 at 17:39
  • 1
    @chux. This was true ~20 years ago. At present, optimizing compilers are incredibly good in their purpose. See [this](https://godbolt.org/z/qdbUzy), for example. – DaBler Dec 12 '18 at 17:50
  • 2
    @DaBler Yes, well aware such a optimization is possible - and have been for 20y, yet OP's goals seem conflicted. On one hand, OP is concerned about _implementation-defined behavior_ yet then wants an answer focusing on a particular implementation (gcc and x86-64) that already meets the goal. To that end, a portable, language specific appears to be sought. 1) To make speedier - in general, a macro is called for 2) potentially using a macro also opens up possibility. – chux - Reinstate Monica Dec 12 '18 at 17:57
  • 1
    [How can I perform arithmetic right shift in C in a portable way?](https://stackoverflow.com/q/31879878/995714), [How to implement arithmetic right shift from logical shift?](https://stackoverflow.com/q/25206670/995714) – phuclv Dec 14 '18 at 02:11
  • nowadays you can hardly find a C implementation that doesn't do arithmetic right shift on a signed type, so you can simply use `assert` to make sure that the shift is arithmetic – phuclv Dec 14 '18 at 02:20

2 Answers2

6
#define USES_ARITHMETIC_SHR(TYPE) ((TYPE)(-1) >> 1 == (TYPE)(-1))

int asr(int value, int amount) /* Better codegen on some older compilers */
{
    return !USES_ARITHMETIC_SHR(int) && value < 0 ? ~(~value >> amount) : value >> amount ;
}

int asr2(int value, int amount) /* Completely portable */
{
    return value < 0 ? ~(~value >> amount) : value >> amount ;
}

This code decides whether to just use the builtin >> operator or not first. You might want to either trust or not trust the preprocessor giving you the same result as the target architecture, but a safe fallback is to not trust it.

Let's explain the value < 0 ? ~(~value >> amount) : value >> amount part.

  1. If value >= 0 then it doesn't matter whether >> is logical or arithmetic, we can use it.
  2. If value < 0 then ~value is the bitwise complement which will be a positive number and (~value >> amount) will be portable (the top amount number of bits will be cleared, the rest shifted right as expected).
    ~(~value >> amount) will flip all the bits back, including flipping the top amount number of zeroes to ones which is exactly what you want with arithmetic right shifting.

The code assuming USES_ARITHMETIC_SHR(int) == true compiles with -O2 into:

asr(int, int): // x86-64 GCC 4.4.7
    mov     eax, edi
    mov     ecx, esi
    sar     eax, cl
    ret
asr(int, int): // x86-64 Clang 3.4.1
    mov     cl, sil
    sar     edi, cl
    mov     eax, edi
    ret
asr(int, int): // ARM GCC 4.5.4
    mov     r0, r0, asr r1
    bx      lr

This should be portable but I'm also unsure if it pedantically really is. If you aren't either, you can #define USES_ARITHMETIC_SHR(TYPE) false or just omit checking it and only check value < 0. But that results in less optimal code on the some older compilers.

The newest version of the compilers (GCC 8+, Clang 7+) compile both versions, asr and asr2 to the same, efficient assembly as above, so you can use either versions of the code. Below is how older compilers do with asr2, a very portable solution.

asr2(int, int): // x86-64 GCC 4.4.7
    test    edi, edi
    js      .L8
    mov     eax, edi
    mov     ecx, esi
    sar     eax, cl
    ret
  .L8:
    mov     eax, edi
    mov     ecx, esi
    not     eax
    sar     eax, cl
    not     eax
    ret
asr2(int, int): // x86-64 Clang 3.4.1
    mov     cl, sil
    sar     edi, cl
    mov     eax, edi
    ret
asr2(int, int): // ARM GCC 4.5.4
    cmp     r0, #0
    mvnlt   r0, r0
    mvnlt   r0, r0, asr r1
    movge   r0, r0, asr r1
    bx      lr
palotasb
  • 4,108
  • 3
  • 24
  • 32
  • Does the `~(~value >> amount)` trick work for any signed integer representation (two's complement, ones' complement, or sign-magnitude)? – DaBler Dec 13 '18 at 17:14
  • No, good point. It's not actually a portable solution for ones' complement. E.g., using ones' complement: `shr(-1, 1) = ~(~-1 >> 1) = ~(1 >> 1) = ~(0) = -0` using https://en.wikipedia.org/wiki/Ones%27_complement -- I _think_ that's not equal to -1, not even with ones' complement, but I'm already too confused be the _existence_ of `-0`. It probably breaks for sign magnitude as well. – palotasb Dec 13 '18 at 20:54
  • 1
    One's complement almost works, except it rounds negative numbers toward zero rather than toward negative infinity. Sign-magnitude is a lost cause, though. Fortunately, in practice, you're not going to see any of those old-timey systems any more. – Raymond Chen Dec 14 '18 at 02:55
  • @RaymondChen Yes, I can confirm exactly what you wrote above. The trick only works on two's complement architectures. However, it is quite easy to check whether the underlying signed integer representation is two's complement in compile time or somewhere early in runtime. – DaBler Dec 14 '18 at 16:22
  • I can also confirm that it compiles into the same code as the code fragment in the question. See this for example: https://godbolt.org/z/6rOBu- – DaBler Dec 14 '18 at 16:23
2

sometime early in your runtime you could check the sanity of your assumption

int check_sanity()
{
    if (~0ll != ~0ll>>8)
    {
        return 0; // not sane
    }
    return 1; // sane
}
Grady Player
  • 14,399
  • 2
  • 48
  • 76
  • 1
    I suggest that over an assert because asserts are usually compiled out of release code. – Grady Player Dec 12 '18 at 16:34
  • 2
    I would put it into the build system. – ctrl-alt-delor Dec 12 '18 at 16:36
  • well it is implementation defined, and I dont think it is required to be the same executing the same code on different processors running the same ABI and same executable... Like it would be legal for a compiler to compile this to code that has different results on different generations of intel or amd processors. – Grady Player Dec 12 '18 at 16:37
  • 1
    The C standard says that the compiler must clearly document, what the code will do. So yes it can do any of the allowed behaviours, so long as it is documented. So if it says in the documentation, that on this processor it will do this, and on that it will do this, then the compiler is compliant. – ctrl-alt-delor Dec 12 '18 at 16:44
  • Indeed, not a bad idea. Since the GCC declares that "Signed ‘>>’ acts on negative numbers by sign extension." ([source](https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html)), one could also check the compiler used to compile the program at compile time. – DaBler Dec 12 '18 at 17:07
  • 1) What advantage do you see with `if (~0ll != ~0ll>>8)` over `if (~0 != ~0>>8)`? 2) Looks like a good `_Static_assert()` candidate. – chux - Reinstate Monica Dec 12 '18 at 17:18
  • _Static_assert() is a compile time thing... people are saying that should be good enough... but there really I would like to see it at runtime also... – Grady Player Dec 13 '18 at 02:08