4

Does the C++ standard mandate that the non-negative range of a standard signed integer type is at least as big as the negative range?

EDIT: Please note that I am referring to the non-negative range here, not the positive range which is obviously one smaller than the non-negative range.

EDIT: If we assume C++11, the answer is "Yes". See my clarifications below. From the point of view of C++03, the answer is probably "No".

The same question can be posed as follows: Does the standard guarantee that the result of a - b is representable in a standard signed integer type T assuming that both a and b are negative values of type T, and that a ≥ b?

I know that the standard allows for two's complement, ones' complement, and sign magnitude representation of negative values (see C++11 section 3.9.1 [basic.fundamental] paragraph 7), but I am not sure whether it demands the use of one of those three representations. Probably not.

If we assume one of these three representations, and we assume that there is no "spurious" restrictions on either of the two ranges (negative, and non-negative), then it is indeed true that the non-negative range is at least as big as the negative one. In fact, with two's complement the size of the two ranges will be equal, and with the two other representations, the size of the non-negative range will be one greater than the size of the negative one.

However, even if we assume one of the mentioned representations, it is really not enough to guarantee anything about the size of either range.

What I am seeking here, is a section (or set of sections) that unambiguously provides the desired guarantee.

Any help will be appreciated.


Note that something like the following would suffice: Every bit within the "storage slot" of the integer has one, and only one of the following functions:

  • Unused
  • Sign bit (exclusively, or mixed sign/value bit)
  • Value bit (participating in the value)

I have a vague memory that C99 says something along those lines. Anyone that knows anything about that?


Alright, C99 (with TC3) does provide the necessary guarantees in section 6.2.6.2 "Integer types" paragraph 2:

For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; there shall be exactly one sign bit. Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type (if there are M value bits in the signed type and N in the unsigned type, then M ≤ N ). If the sign bit is zero, it shall not affect the resulting value. If the sign bit is one, the value shall be modified in one of the following ways:

  • the corresponding value with sign bit 0 is negated (sign and magnitude);
  • the sign bit has the value −(2N ) (two’s complement);
  • the sign bit has the value −(2N − 1) (ones’ complement ).

Which of these applies is implementation-defined, as is whether the value with sign bit 1 and all value bits zero (for the first two), or with sign bit and all value bits 1 (for ones’ complement), is a trap representation or a normal value. In the case of sign and magnitude and ones’ complement, if this representation is a normal value it is called a negative zero.

Can someone confirm that this part of C99 is also a binding part of C++11?


I have taken another careful look at both the C99 and the C++11 standards, and it is clear that the guarantees in C99 section 6.2.6.2 paragraph 2 are binding in C++11 too.

C89/C90 does not provide the same guarantees, so we do need C99, which means that we do need C++11.

In summary, C++11 (and C99) provides the following guarantees:

  1. Negative values in fundamental signed integers types (standard + extended) must be represented using one of the following three representations: Two's complement, ones' complement, or sign magnitude.

  2. The size of the non-negative range is one greater than, or equal to the size of the negative range for all fundamental signed integers types (standard + extended).

The second guarantee can be restated as follows:

-1 ≤ min<T> + max<T> ≤ 0

for any fundamental signed integers type T (standard + extended) where min<T> and max<T> are shorthands for std::numeric_limits<T>::min() and std::numeric_limits<T>::max() respectively.

Also, if we assume that a and b are values of the same, or of different fundamental signed integer types (standard or extended), then it follows that a - b is well defined and representable in decltype(a - b) as long as a and b are either both negative or both non-negative.

Kristian Spangsege
  • 2,903
  • 1
  • 20
  • 43

1 Answers1

3

The standard does seem to not mandate such a thing although I may be missing key passages. All we know about fundamental signed integral types is in 3.9.1/2:

There are five standard signed integer types : “signed char”, “short int”, “int”, “long int”, and “long long int”. In this list, each type provides at least as much storage as those preceding it in the list.

And in 3.9.1/7:

Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.48 A synonym for integral type is integer type. The representations of integral types shall define values by use of a pure binary numeration system.

Neither of these passages seem to say anything about the respective positive and negative ranges. Even given that I can't conceive of a binary representation that wouldn't meet your needs.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • Yes, and unfortunately, the definition of "pure binary numeration system" in footnote 49 is too weak to provide any useful information on this matter. – Kristian Spangsege Mar 05 '14 at 17:47
  • 1
    Well, I could think of tons of different binary representations that wouldn't meet the OP's needs: Take an unsigned integer type, and define all numbers above any arbitrary number to be negative numbers. Much in the same way as 2's complement defines all numbers beyond `0x7fffffff` to be negative, just use a smaller constant... Let's just hope, no CPU manufacturer ever implements something like this. – cmaster - reinstate monica Mar 05 '14 at 17:49
  • @cmaster True, so the question is whether the standard intends to allow such representations, or not. – Kristian Spangsege Mar 05 '14 at 17:51
  • I wonder whether some of the necessary guarantees can be "lifted" from the C standard. – Kristian Spangsege Mar 05 '14 at 19:53