2

Is this a safe way to determine the maximum value of an integer without using the limits library?

int max_int = (unsigned int) -1 >> 1;
Massimiliano
  • 7,842
  • 2
  • 47
  • 62
Stamoulohta
  • 649
  • 1
  • 7
  • 21

1 Answers1

0

I believe that the code you proposed:

int max_int = (unsigned int) -1 >> 1;

is not a safe way to determine the maximum value of a signed integer type (according to the standard), but it should definitely work on most architectures. I'll try to carefully motivate the statement below.

The first thing to notice is that:

6.2.5 Types

...

  • For each of the signed integer types, there is a corresponding (but different) unsigned integer type (designated with the keyword unsigned) that uses the same amount of storage (including sign information) and has the same alignment requirements. ...

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

These lines of the standard ensure that (unsigned int) -1 is converted to UINT_MAX. Then the following:

6.5.7 Bitwise shift operators

...

  • The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2^E2.

ensures that the value of the number is divided by 2. Now it comes the tricky point. According to the following:

6.2.6.2 Integer types

  • 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). If there are N value bits, each bit shall represent a different power of 2 between 1 and 2N-1, so that objects of that type shall be capable of representing values from 0 to 2N - 1 using a pure binary representation; this shall be known as the value representation. The values of any padding bits are unspecified.
  • 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).

signed integer types are composed of:

  • M value BITS
  • X padding bits
  • 1 sign bit

while unsigned integer types are composed of:

  • N value BITS
  • Y padding bits

Due to the fact that signed and unsigned integers of corresponding types must have the same storage, I argue that M+X+1 == N+Y.

Your assumption, when shifting of only one bit is that M = N - 1 or, otherwise stated, that the number of padding bits in an int is the same as that of an unsigned int.

Now, given the conversion rule:

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.

if your assumption is correct on the architecture you are working on, you will hit the first bullet, otherwise you'll hit the third and the behavior will be implementation defined.

The latter option won't be "safe", as you can't ensure a unique behavior for your program based on the C standard only, and you can't rely on an implementation to have some feature you may need to handle that case properly.

Massimiliano
  • 7,842
  • 2
  • 47
  • 62
  • This should be a comment. OP asked how to; without limits.h. – this Jan 12 '14 at 11:15
  • The question was without using the limits library. – The Wavelength Jan 12 '14 at 11:15
  • So when is that not a safe way? Looks like it is always defined. – this Jan 12 '14 at 23:52
  • @self. When you are on an implementation where the number of value bits for an `unsigned int` is not 1 more than the number of value bits for an `int` the behavior is _implementation defined_. This is in my opinion not safe, as you can't ensure a unique behavior for your program based on the C standard only. – Massimiliano Jan 13 '14 at 06:20
  • @self. I am not sure I understand your comment. The OP asked if its line was a safe way to determine `INT_MAX`. The answer is _it is not safe, as you assume a shift of one bit which, according to the standard, may be wrong on some implementation_. I really don't understand where the standard library comes in... – Massimiliano Jan 13 '14 at 06:44
  • @self. Then you agree that this is not a _safe way to determine the maximum value of an integer_, as the implementation defined behavior won't ensure that `max_int` will be always equal to `INT_MAX`? – Massimiliano Jan 13 '14 at 06:48
  • What don't I understand and people who downvote do in this answer? – Stamoulohta Jan 13 '14 at 08:04
  • http://port70.net/~nsz/c/c11/n1570.html#6.2.6.2 prescribes the representation of signed integers as one of: 1) sign and magnitude 2) one's complement, 3) two's complement. In any of the three the maximum value of an N-bits-wide signed integer is 2 to the (N-1)th power minus 1. You'll get that by `>>1`-shifting the max value of the corresponding unsigned. Since int cannot have a different representation than one of the 3 mentioned above, it's maximum must be `(unsigned int) -1 >> 1` and so `(unsigned int) -1 >> 1` must be representable as `int` and you can't ever hit the 3rd bullet. – Petr Skocik Nov 25 '18 at 17:12