What is maximum guaranteed range of N-bit signed integer? - c++

Some background:
Prior to C++20, the C++ Standard allowed any signed integer representation, and the minimum guaranteed range of N-bit signed integers was from -(2^(N-1) - 1) to +2^(N-1) - 1.
C++20 standard proposal (as mentioned by #Sneftel) allowed the only representation with the guaranteed range from -2^(N-1) to +2^(N-1) - 1.
As far as I understand the range of values min-max must be continuous.
I've tried to find any quotes for the maximum range of N-bit signed integer but found none.
So I wonder (in terms of C++):
Is it right to assume that maximum possible range of N-bit signed integer is limited from -2^(N-1) to +2^(N-1)? Could it be wider?
Could it be affected by underlying platform bit implementation - like "3-state-bit" or something like that?

C++2a, while projected to become the C++20 standard, is not there yet.
Aside from that, while the current draft enforces two's-complement representation for signed integers, the current Standard (C++17) and all precursors do restrict choice:
Specifically, the other options offered are signed magnitude representation and ones' complement. Both feature negative zero.
There is information on the range of N-bit signed integers. Specifically, that not all of those N1 bits of the object-representation need be part of the N2 bits of the value-representation, with the exception of the narrow character types. So, the value-representation's N-bit width might be smaller. Considering the pigeonhole-principle, it is no surprise it cannot be bigger.
The assumption that bits are binary is deeply embedded in the language, just like it pervades many other programming-languages. Changing that would be a major project.

There is not currently a final C++20 standard. The proposal you're likely thinking of is P1236, and it says:
The range of representable values for a signed integer type is -2^(N-1) to 2^(N-1)-1 (inclusive), where N is called the range exponent of the type.
That's both a maximum and a minimum. Because the proposal requires two's complement for signed integers, there's no opportunity for variation. Trivially, N bits cannot be used to represent more than 2^N distinct values (by the pigeonhole principle), so that range is as big as things can get, even if it were specified only as a minimum range.
As for three-state bits, those don't work with the language.

Related

Does the C++ standard requires signed integers to have exactly one sign bit?

Consider the fundamental signed integer types of C++, namely: signed char, short int, int, long int and long long int, what does the current C++ standard require about their underlying bit representation?
Does the constraints on their bit representation specify that they should include:
optional padding bits
mandatory value bits
a mandatory sign bit that is 0 for positive values, and 1 for negative value
if it exists, the sign bit should be the most significant bit
Is this true? If not, then what are the constaints? I am searching for quotes from the standard that proves or disproves this.
EDIT: I am asking this question, because, in C, the standard says:
6.2.6.2.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; signed char shall not have
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 −(2^M ) (two’s complement);
the sign bit has the value −(2^M − 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.
So I am wondering whether something comparable exists in C++
This is what C++11 says about representation of signed integer types:
C++11 N3337 3.9.1 [basic.fundamental] P7:
The representations of integral types
shall define values by use of a pure binary numeration system. 49 [ Example: this International Standard permits 2’s complement, 1’s complement and signed magnitude representations for integral types. — end
example ]
where Footnote 49 reads:
A positional representation for integers that uses the binary digits 0 and 1, in which the values represented by successive bits are additive, begin with 1, and are multiplied by successive integral power of 2, except perhaps for the bit with the highest
position. (Adapted from the American National Dictionary for Information Processing Systems.)
Thus C++ allows the same three options as C, as well as anything else satisfying Footnote 49. Which is a superset of what C allows. By Footnote 49, however, only the highest bit is allowed to have special meaning.
Update 2021-10-11
C++20 has introduced changes regarding allowed representations of signed integers.
N4860 [basic.fundamental/p3] reads:
An unsigned integer type has the same object representation, value representation, and alignment requirements
(6.7.6) as the corresponding signed integer type. For each value x of a signed integer type, the value of the
corresponding unsigned integer type congruent to x modulo 2 N has the same value of corresponding bits in
its value representation. 40 [Example: The value −1 of a signed integer type has the same representation as
the largest value of the corresponding unsigned type. — end example]
And the footnote 40 reads:
This is also known as two’s complement representation.
Therefore, C++ now mandates two's complement representation of signed integer types.
I'm guessing the answer to the question you asked is no.
I think the C++ Standard specifies the minimum size, and the range of values that each integer type must be able to represent. I don't believe the standard speaks specifically to any of the constraints you list.
I think those are all implementation details.
I think it would be odd to find a C++ implementation that used more than a single bit to hold the sign, and not use a 0 for positive and 1 for negative. But I don't think the C++ Standard specifically requires it.
The C++ Standard is based in particularly on the C Standard where there is written (6.2.6.2 Integer types)
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; signed char shall not
have any padding bits. There shall be exactly one sign bit.....
The requirement that there be exactly one sign bit means that it must be possible to identify a bit which is set for all negative numbers, and clear for all non-negative numbers. An implementation may include within an "int" any number of padding bits, impose arbitrary restrictions on their values, and treat as trap representations any bit patterns that violate those requirements, provided that all calculations that yield defined integer values produce bit patterns that the implementation will accept.
For example, an implementation could store "int" as two 16-bit words and specifies that the MSB of of the first word is the sign bit. Such an implementation could write 0-14 of the first word match the sign bit and
trap when reading any value where they don't, or make those bits match bits
1-15 of the second word (likewise trapping), or could write arbitrary values
to those bits and ignore them when reading, or do just about anything else
with them. If an implementation always wrote the top word as all ones or
all zero, any bit could be designated the "sign bit" and it wouldn't matter;
the rest would all be "padding bits".
The requirement that there be a single sign bit would mainly rule out
implementations where e.g. positive numbers may arbitrarily be represented
as bit pattern 00 or 11, and negative numbers as 01 or 10. On such an
implementation, it would be necessary to examine two bits rather than one
to determine whether a number was negative or non-negative.

Is the non-negative range of a signed C++ integer at least as big as the negative range?

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:
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.
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.
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.

Why is unsigned integer overflow defined behavior but signed integer overflow isn't?

Unsigned integer overflow is well defined by both the C and C++ standards. For example, the C99 standard (§6.2.5/9) states
A computation involving unsigned operands can never overflow,
because a result that cannot be represented by the resulting unsigned integer type is
reduced modulo the number that is one greater than the largest value that can be
represented by the resulting type.
However, both standards state that signed integer overflow is undefined behavior. Again, from the C99 standard (§3.4.3/1)
An example of undefined behavior is the behavior on integer overflow
Is there an historical or (even better!) a technical reason for this discrepancy?
The historical reason is that most C implementations (compilers) just used whatever overflow behaviour was easiest to implement with the integer representation it used. C implementations usually used the same representation used by the CPU - so the overflow behavior followed from the integer representation used by the CPU.
In practice, it is only the representations for signed values that may differ according to the implementation: one's complement, two's complement, sign-magnitude. For an unsigned type there is no reason for the standard to allow variation because there is only one obvious binary representation (the standard only allows binary representation).
Relevant quotes:
C99 6.2.6.1:3:
Values stored in unsigned bit-fields and objects of type unsigned char shall be represented using a pure binary notation.
C99 6.2.6.2:2:
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) (one’s complement).
Nowadays, all processors use two's complement representation, but signed arithmetic overflow remains undefined and compiler makers want it to remain undefined because they use this undefinedness to help with optimization. See for instance this blog post by Ian Lance Taylor or this complaint by Agner Fog, and the answers to his bug report.
Aside from Pascal's good answer (which I'm sure is the main motivation), it is also possible that some processors cause an exception on signed integer overflow, which of course would cause problems if the compiler had to "arrange for another behaviour" (e.g. use extra instructions to check for potential overflow and calculate differently in that case).
It is also worth noting that "undefined behaviour" doesn't mean "doesn't work". It means that the implementation is allowed to do whatever it likes in that situation. This includes doing "the right thing" as well as "calling the police" or "crashing". Most compilers, when possible, will choose "do the right thing", assuming that is relatively easy to define (in this case, it is). However, if you are having overflows in the calculations, it is important to understand what that actually results in, and that the compiler MAY do something other than what you expect (and that this may very depending on compiler version, optimisation settings, etc).
First of all, please note that C11 3.4.3, like all examples and foot notes, is not normative text and therefore not relevant to cite!
The relevant text that states that overflow of integers and floats is undefined behavior is this:
C11 6.5/5
If an exceptional condition occurs during the evaluation of an
expression (that is, if the result is not mathematically defined or
not in the range of representable values for its type), the behavior
is undefined.
A clarification regarding the behavior of unsigned integer types specifically can be found here:
C11 6.2.5/9
The range of nonnegative values of a signed integer type is a subrange
of the corresponding unsigned integer type, and the representation of
the same value in each type is the same. A computation involving
unsigned operands can never overflow, because a result that cannot be
represented by the resulting unsigned integer type is reduced modulo
the number that is one greater than the largest value that can be
represented by the resulting type.
This makes unsigned integer types a special case.
Also note that there is an exception if any type is converted to a signed type and the old value can no longer be represented. The behavior is then merely implementation-defined, although a signal may be raised.
C11 6.3.1.3
6.3.1.3 Signed and unsigned integers
When a value with integer
type is converted to another integer type other than _Bool, if the
value can be represented by the new type, it is unchanged.
Otherwise, if the new type is unsigned, the value is converted by
repeatedly adding or subtracting one more than the maximum value that
can be represented in the new type until the value is in the range of
the new type.
Otherwise, the new type is signed and the value
cannot be represented in it; either the result is
implementation-defined or an implementation-defined signal is raised.
In addition to the other issues mentioned, having unsigned math wrap makes the unsigned integer types behave as abstract algebraic groups (meaning that, among other things, for any pair of values X and Y, there will exist some other value Z such that X+Z will, if properly cast, equal Y and Y-Z will, if properly cast, equal X). If unsigned values were merely storage-location types and not intermediate-expression types (e.g. if there were no unsigned equivalent of the largest integer type, and arithmetic operations on unsigned types behaved as though they were first converted them to larger signed types, then there wouldn't be as much need for defined wrapping behavior, but it's difficult to do calculations in a type which doesn't have e.g. an additive inverse.
This helps in situations where wrap-around behavior is actually useful - for example with TCP sequence numbers or certain algorithms, such as hash calculation. It may also help in situations where it's necessary to detect overflow, since performing calculations and checking whether they overflowed is often easier than checking in advance whether they would overflow, especially if the calculations involve the largest available integer type.
Perhaps another reason for why unsigned arithmetic is defined is because unsigned numbers form integers modulo 2^n, where n is the width of the unsigned number. Unsigned numbers are simply integers represented using binary digits instead of decimal digits. Performing the standard operations in a modulus system is well understood.
The OP's quote refers to this fact, but also highlights the fact that there is only one, unambiguous, logical way to represent unsigned integers in binary. By contrast, Signed numbers are most often represented using two's complement but other choices are possible as described in the standard (section 6.2.6.2).
Two's complement representation allows certain operations to make more sense in binary format. E.g., incrementing negative numbers is the same that for positive numbers (expect under overflow conditions). Some operations at the machine level can be the same for signed and unsigned numbers. However, when interpreting the result of those operations, some cases don't make sense - positive and negative overflow. Furthermore, the overflow results differ depending on the underlying signed representation.
The most technical reason of all, is simply that trying to capture overflow in an unsigned integer requires more moving parts from you (exception handling) and the processor (exception throwing).
C and C++ won't make you pay for that unless you ask for it by using a signed integer. This isn't a hard-fast rule, as you'll see near the end, but just how they proceed for unsigned integers. In my opinion, this makes signed integers the odd-one out, not unsigned, but it's fine they offer this fundamental difference as the programmer can still perform well-defined signed operations with overflow. But to do so, you must cast for it.
Because:
unsigned integers have well defined overflow and underflow
casts from signed -> unsigned int are well defined, [uint's name]_MAX - 1 is conceptually added to negative values, to map them to the extended positive number range
casts from unsigned -> signed int are well defined, [uint's name]_MAX - 1 is conceptually deducted from positive values beyond the signed type's max, to map them to negative numbers)
You can always perform arithmetic operations with well-defined overflow and underflow behavior, where signed integers are your starting point, albeit in a round-about way, by casting to unsigned integer first then back once finished.
int32_t x = 10;
int32_t y = -50;
// writes -60 into z, this is well defined
int32_t z = int32_t(uint32_t(y) - uint32_t(x));
Casts between signed and unsigned integer types of the same width are free, if the CPU is using 2's compliment (nearly all do). If for some reason the platform you're targeting doesn't use 2's Compliment for signed integers, you will pay a small conversion price when casting between uint32 and int32.
But be wary when using bit widths smaller than int
usually if you are relying on unsigned overflow, you are using a smaller word width, 8bit or 16bit. These will promote to signed int at the drop of a hat (C has absolutely insane implicit integer conversion rules, this is one of C's biggest hidden gotcha's), consider:
unsigned char a = 0;
unsigned char b = 1;
printf("%i", a - b); // outputs -1, not 255 as you'd expect
To avoid this, you should always cast to the type you want when you are relying on that type's width, even in the middle of an operation where you think it's unnecessary. This will cast the temporary and get you the signedness AND truncate the value so you get what you expected. It's almost always free to cast, and in fact, your compiler might thank you for doing so as it can then optimize on your intentions more aggressively.
unsigned char a = 0;
unsigned char b = 1;
printf("%i", (unsigned char)(a - b)); // cast turns -1 to 255, outputs 255

What do the C and C++ standards say about bit-level integer representation and manipulation?

I know the C and C++ standards don't dictate a particular representation for numbers (could be two's complement, sign-and-magnitude, etc.). But I don't know the standards well enough (and couldn't find if it's stated) to know if there are any particular restrictions/guarantees/reserved representations made when working with bits. Particularly:
If all the bits in an integer type are zero, does the integer as whole represent zero?
If any bit in an integer type is one, does the integer as a whole represent non-zero? (if this is a "yes" then some representations like sign-and-magnitude would be additionally restricted)
Is there a guaranteed way to check if any bit is not set?
Is there a guaranteed way to check if any bit is set? (#3 and #4 kind of depend on #1 and #2, because I know how to set, for example the 5th bit (see #5) in some variable x, and I'd like to check a variable y to see if it's 5th bit is 1, I would like to know if if (x & y) will work (because as I understand, this relies on the value of the representation and not whether nor not that bit is actually 1 or 0))
Is there a guaranteed way to set the left-most and/or right-most bits? (At least a simpler way than taking a char c with all bits true (set by c = c | ~c) and doing c = c << (CHAR_BIT - 1) for setting the high-bit and c = c ^ (c << 1) for the low-bit, assuming I'm not making any assumptions I should't be, given these questions)
If the answer to #1 is "no" how could one iterate over the bits in an integer type and check if each one was a 1 or a 0?
I guess my overall question is: are there any restrictions/guarantees/reserved representations made by the C and C++ standards regarding bits and integers, despite the fact that an integer's representation is not mandated (and if the C and C++ standards differ in this regard, what's their difference)?
I came up with these questions while doing my homework which required me to do some bit manipulating (note these aren't questions from my homework, these are much more "abstract").
Edit: As to what I refer to as "bits," I mean "value forming" bits and am not including "padding" bits.
(1) If all the bits in an integer type are zero, does the integer as whole represent zero?
Yes, the bit pattern consisting of all zeroes always represents 0:
The representations of integral types shall define values by use of a pure binary numeration system.49 [§3.9.1/7]
49 A positional representation for integers that uses the binary digits 0 and 1, in which the values represented by successive bits are additive, begin with 1, and are multiplied by successive integral power of 2, except perhaps for the bit with the highest position.
(2) If any bit in an integer type is one, does the integer as a whole represent non-zero? (if this is a "yes" then some representations like sign-and-magnitude would be additionally restricted)
No. In fact, signed magnitude is specifically allowed:
[ Example: this International Standard permits 2’s complement, 1’s complement and signed magnitude representations for integral types. —end
example ] [§3.9.1/7]
(3) Is there a guaranteed way to check if any bit is not set?
I believe the answer to this is "no," if you consider signed types. It is equivalent to equality testing with a bit pattern of all ones, which is only possible if you have a way to produce a signed number with bit pattern of all ones. For an unsigned number this representation is guaranteed, but casting from unsigned to signed is undefined if the number is unrepresentable:
If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined. [§4.7/3]
(4) Is there a guaranteed way to check if any bit is set?
I don't think so, because signed magnitude is allowed—0 would compare equal to −0. But it should be possible with unsigned numbers.
(5) Is there a guaranteed way to set the left-most and/or right-most bits?
Again, I believe the answer is "yes" for unsigned numbers, but "no" for signed numbers. Shifts are undefined for negative signed numbers:
Otherwise, if E1 has a signed type and non-negative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined. [§5.8/2]
You use the term "all bits" repeatedly, but you do not clarify what "all bits" you are referring to. Object representation of integer types in C/C++ might include value-forming bits and padding bits. The only integer type that is guaranteed not to have padding bits is [signed/unsigned] char.
The language always guaranteed that if all value-forming bits are zero, then the represented integer value is also zero.
As for padding bits, things are/were a bit more complicated. The original specification of C language (C89/90 as well as the original C99) did not guarantee that setting all object bits to zero produced a valid integer representation. It could've produced an invalid trap representation. I.e. in the original C (and even in C99 at first) using memset(..., 0, ...) on integer types did not guarantee that the objects will receive valid zero values (with the exception of [signed/unsigned] char). This was changed in later specifications, namely in one of the technical corrigendums for C99. Now it is required that all-zero bit pattern in an integer object (that involves all bits, including padding ones) represents a valid zero value.
I.e. in modern C it is legal to use memset(..., 0, ...) to set any integer objects to zero, but it became legal only after C99.
You already got some answers about the representation of integer values. There is exactly one way that is guaranteed to give you all the individual bits of any object that is represented in memory: view it as array of unsigned char. This is the only integral type that has no padding bits and is guaranteed to have no trap representation. So casting a pointer of type T* to your object to unsigned char* will always work, as long as you only access the first sizeof(T) bytes. By that you could inspect and set all bytes (and thus bits) to your liking.
If you are interested in more details, here I have written something up about the anatomy of integer types in C. C++ might differ a bit from that, in particular type puning through union as described there doesn't seem to be well defined in C++.
Q: If any bit in an integer type is one, does the integer as a whole represent non-zero? (if this is a "yes" then some representations like sign-and-magnitude would be additionally restricted)
No. The standards for C and C++ don't rule out signed magnitude or one's complement, both of which have +0 and -0. While +0 and -0 do have to compare equal, but they do not have to have the same representation.
Good luck finding a machine nowadays that uses signed magnitude or one's complement.
If you want your brain to explode, consider this: If you interpret an int or long or long long as an array of unsigned char (which is the most reasonable thing to do if you want to see all the bits), you know that the order of bytes is not defined, for example "bigendian" vs. "littleendian". We all (hopefully) know that.
But it is worse: Each bit of an int could be stored in any of the bits of the array of char. So there are 32! ways how the bits of a 32 bit integer could be mapped to an array of four 8-bit unsigned chars by a truly bizarre implementation. Fortunately, I haven't encountered more than two ways myself (and I know of one more ordering in a real computer).
If all the bits in an integer type are zero, does the integer as whole represent zero?
Edit: since you have now clarified that you are not concerned with the padding bits, the answer to this is actually "yes". But I leave the original:
Not necessarily, it could be a trap representation. See C99 6.2.6.1:
For unsigned integer types other than unsigned char, the bits of the object
representation shall be divided into two groups: value bits and padding bits (there need not be any of the latter)
The presence of padding bits allows for the possibility that all 0 is a trap representation. (As noted by Keith Thompson in the comment below, the more recent C11 makes explicit that such a representation is not a trap representation).
and
The values of any padding bits are unspecified
and
44) Some combinations of padding bits might generate trap representations
If you restrict the question to value and sign bits, the answer is yes, due to 6.2.6.2:
If there are N value bits, each bit shall represent a different
power of 2 between 1 and 2 N −1 , so that objects of that type shall be capable of representing values from 0 to 2 N − 1 using a pure binary representation; this shall be known as the value representation.
and
If the sign bit is zero, it shall not affect the resulting value.
If any bit in an integer type is one, does the integer as a whole represent non-zero? (if this is a "yes" then some representations like sign-and-magnitude would be additionally restricted)
Not necessarily, and in fact sign-and-magnitude is explicitly supported in 6.2.6.2.
Is there a guaranteed way to check if any bit is not set?
If you do not care about padding and sign bits, you could just compare to 0, but this would not work with a 1's complement representation (which is allowed) seeing as all bits 0 and all bits 1 both represent the value 0.
Otherwise: you can read the value of each byte via an unsigned char *, and compare the result to 0:
Values stored in unsigned bit-fields and objects of type unsigned char
shall be represented using a pure binary notation
If you want to check a specific value bit, you could construct a suitable bitmask using (1u << n), but this will not necessarily let you inspect the sign bit.
Is there a guaranteed way to check if any bit is set?
The answer is essentially the same as to the previous question.
Is there a guaranteed way to set the left-most and/or right-most bits?
Do you mean left-most value bit? You could count the bits in INT_MAX or UINT_MAX or equivalent depending on the type, and use that to construct a value (via 1 << n) with which to OR the original value.
If the answer to #1 is "no" how could one iterate over the bits in an integer type and check if each one was a 1 or a 0?
You can do so using a bitmask which you left shift repeatedly, but you can check only the value bits this way and not the sign bit.
For the bitmanipulations you could make a struct with 8 one unsigned bit fields and let the pointer of that struct point to your char. In that way you can easily access each bit. But the compiler will probably do masking under the hood, so it is only a cleaner way for the programmer I think. You must check that your compiler doesn't change the order of the fields when doing this.
yourstruct* pChar=(yourstruct*)(&c)
pChar.Bit7=1;
Let me caveat this by saying I'm addressing C and C++ in general (e.g. C90 and lower, MS Visual C++, etc): the "greatest common denominator" (vs. the latest/greatest cx11 "standard").
Q: If all the bits in an integer type are zero, does the integer as whole represent zero?
A: Yes
Q: If any bit in an integer type is one, does the integer as a whole represent non-zero? (if this is a "yes" then some representations like sign-and-magnitude would be additionally restricted)
A: Yes. This includes the sign bit, for a signed int.
I'm frankly not familiar with "magnitude"
Q: Is there a guaranteed way to check if any bit is not set?
A: "And'ing" a bitmask is always guaranteed.
Q: Is there a guaranteed way to check if any bit is set?
A: Again, "and'ing" a bitmask is always guaranteed.
Q: Is there a guaranteed way to set the left-most and/or right-most bits?
A: I believe you should always have a "MAX_INT" available for all implementations/all architectures to determine the leftmost bit.
I'm prepared to be flamed ... but I believe the above is accurate. And I hope it helps.
IMHO...

Why numeric_limits<int>::min() is differently defined?

To retrieve the smallest value i have to use numeric_limits<int>::min()
I suppose the smallest int is -2147483648, and tests on my machine showed this result.
But some C++ references like Open Group Base Specifications and
cplusplus.com define it with the value -2147483647.
I ask this question because in my implementation of the negaMax Framework (Game Tree Search)
the value minimal integer * (-1) has to be well defined.
Yes, with minimal int = (numeric_limits::min() + 2) i am on the safe side in any case,
thus my question is more theoretically but i think nevertheless quite interesting.
If a value is represented as sign-and-magnitude instead of two's complement, the sign bit being one with all other bits as zero is equivalent to -0. In sign-and-magnitude the maximum positive integer and negative integer are the same magnitude. Two's complement is able to represent one more negative value because it doesn't have the same symmetry.
The value of numeric_limits<int>::min() is defined by implementation. That's why it could be different. You shouldn't stick to any concrete minimal value.
On cplusplus.com you forgot to read the qualifier
min. magnitude*
This is not necessarily the actual value of the constant in any particular compiler or system, it may be equal or greater in magnitude than this.
From the cplusplus.com link you posted (emphasis mine):
The following panel shows the different constants and their guaranteed minimal magnitudes (positive numbers may be greater in value, and negative numbers may be less in value). Any particular compiler implementation may define integral types with greater magnitudes than those shown here
Numeric limits are always system and compiler defined, try running with a 64bit compiler and system, you may see totally different numbers.
c++ uses two-s compliment for signed integers. Thus the smallest signed integer is defined by 100..00 (usually 32 bit).
Simply shifting 1<<(sizeof(int)*8-1) should give you the smallest signed integer.
Obviously for unsigned integers, the smallest is 0.
edit: you can read more here
edit2: apparently C++ doesn't necessarily use two-s compliment, my mistake