C++ Logo

sg12

Advanced search

Re: [ub] Signed shifting

From: Samuel Neves <sneves_at_[hidden]>
Date: Tue, 18 Nov 2014 01:04:43 +0000
On 17-11-2014 23:14, Howard Hinnant wrote:
> Examples of real-world code that want to do arithmetic right shifts on both positive and negative values. This code is portable across all two’s complement machines, assuming that the compiler does not take advantage of the current ub.

For what it's worth, there is currently no UB in those examples; signed right shift is implementation-defined, as
mentioned upthread. If one is feeling paranoid, such functions can be 'fixed' by using unsigned shifts only. Here's an
example for the sign function:

int sign(int x) {
    constexpr int n = std::numeric_limits<int>::digits;
    const unsigned int u = x;
    return -(u >> n) | ((u|-u) >> n);
}

There is yet another implementation-defined behavior in those examples that has not been brought up: conversion from an
unsigned integer outside of positive signed range (e.g., 0xFF...FF) to a signed integer (N4140 §4.7/3). One can ensure
the conversion always works with the following workaround:

int sign(int x) {
    constexpr int n = std::numeric_limits<int>::digits;
    const unsigned int u = x;
    const int s = 1 + (-(u >> n) | ((u|-u) >> n)); // {0, 1, 2}
    return s - 1; // {-1, 0, 1}
}

The situation is worse with signed left shifts, which actually invoke undefined (as opposed to implementation-defined)
behavior. C++14 (DR1457) already partially fixed the signed left shift situation by allowing shifts into (but not over)
the sign bit--this allows to define INT_MIN as, e.g., 1 << 31 without running into UB. I don't see why this relaxation
cannot be extended to shifts that go beyond the sign bit.

Received on 2014-11-18 02:23:31