C++ Logo

sg12

Advanced search

Re: [ub] Signed shifting

From: Howard Hinnant <howard.hinnant_at_[hidden]>
Date: Mon, 17 Nov 2014 15:24:18 -0500
On Nov 17, 2014, at 2:11 PM, Jeffrey Yasskin <jyasskin_at_[hidden]> wrote:

> * The last thread on two's-complement representation managed to find
> one modern Unisys system that uses one's-complement, and the thread
> reported that this one supports Java, so it has to have a way to
> execute two's-complement shifts. We shouldn't be building a standard
> for imaginary systems.

Agreed.

>
> * Even if 'int' needs to support non-two's-complement, the referenced
> code (https://github.com/teor2345/tor/blob/ced74e0144e967f838416e6af92cba65c007d89b/src/ext/curve25519_donna/curve25519-donna.c#L56)
> uses int64_t, which <C99 7.18.1.1 Exact-width integer types> says is
> already two's complement. C++14 defers to C99 for this. We could
> define shifts on two's complement types that would stay
> implementation-defined on anything else.
>
> * <snip> Without
> a good reason, we shouldn't make them use worse syntax for this, even
> for signed numbers. "Applying [bit-shifts] to signed numbers is a bug"
> is just incorrect given compilers' and hardware's actual behavior.
> (Appealing to the fact that it's undefined behavior in the current
> standard is just begging the question.)

Agreed most strongly.

Out of the entire computer science industry, this seems to be the only forum where such an opinion (that shifting signed numbers is a bug) is held. Elsewhere, this is known as an arithmetic shift:

http://en.wikipedia.org/wiki/Arithmetic_shift

It is supported on all modern hardware I am aware of. In more detail, all modern hardware will perform the following three shifts in a portable manner as long as the number of bits shifted is less than the width of the word being shifted:

Arithmetic right shift
Logical right shift
Logical left shift

I see no reason not to allow all three of these shifts in C++, as long as the number of bits shifted is less than the word size.

I also see no reason to define shift operations in terms of multiplying or dividing by a power of 2. Doing so unnecessarily couples the integral bit representation with shift operations. Instead the shift operators should be defined in terms of shifting bits left or right, and what gets filled into a vacated bit position.

Furthermore, where platforms give well-defined but non-portable results, the C++ standard should call that implementation-defined behavior, not undefined behavior. For example an arithmetic right shift might be different for negative values on two’s complement and one’s complement machines, but it is well defined on each individual platform. Undefined behavior should be reserved *only* for the cases where we really have no idea what might happen.

Two’s complement machines are sufficiently prevalent that most programmers can write for them secure in the knowledge that their code is *sufficiently portable* for their purposes. And there is absolutely no rationale for penalizing this massive number of programmers with undefined behavior, which compilers may silently and arbitrarily rewrite into unintended programs.

I consider this (and other similar over-reaches of undefined behavior) one of the most urgent issues facing C++ today. Jeffrey’s case is not even close to an isolated incident.

Howard

Received on 2014-11-17 21:24:23