31

How can I portably find out the smallest of INT_MAX and abs(INT_MIN)? (That's the mathematical absolute value of INT_MIN, not a call to the abs function.)

It should be as same as INT_MAX in most systems, but I'm looking for a more portable way.

Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
  • 3
    @barakmanos: They're not the same; usually, `INT_MIN == -INT_MAX - 1`. I think it's actually UB to take `-INT_MIN` on such systems. – user2357112 Apr 22 '15 at 23:59
  • 1
    you mean the `abs` function 2's complement which returns the same type as the argument so `abs(INT_MIN) == INT_MIN` or abs in the mathematical sense, which might need a wider type to return the correct value of `min(INT_MAX, abs(INT_MIN))`? – phuclv Apr 23 '15 at 09:01

6 Answers6

23

While the typical value of INT_MIN is -2147483648, and the typical value of INT_MAX is 2147483647, it is not guaranteed by the standard. TL;DR: The value you're searching for is INT_MAX in a conforming implementation. But calculating min(INT_MAX, abs(INT_MIN)) isn't portable.


The possible values of INT_MIN and INT_MAX

INT_MIN and INT_MAX are defined by the Annex E (Implementation limits) 1 (C standard, C++ inherits this stuff):

The contents of the header are given below, in alphabetical order. The minimum magnitudes shown shall be replaced by implementation-defined magnitudes with the same sign. The values shall all be constant expressions suitable for use in #if preprocessing directives. The components are described further in 5.2.4.2.1.

[...]

#define INT_MAX +32767

#define INT_MIN -32767

[...]

The standard requires the type int to be an integer type that can represent the range [INT_MIN, INT_MAX] (section 5.2.4.2.1.).

Then, 6.2.6.2. (Integer types, again part of the C standard), comes into play and further restricts this to what we know as two's or ones' complement:

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 −(2M) (two’s complement);

— the sign bit has the value −(2M − 1) (ones’ complement).

Section 6.2.6.2. is also very important to relate the value representation of the signed integer types with the value representation of its unsigned siblings.

This means, you either get the range [-(2^n - 1), (2^n - 1)] or [-2^n, (2^n - 1)], where n is typically 15 or 31.

Operations on signed integer types

Now for the second thing: Operations on signed integer types, that result in a value that is not within the range [INT_MIN, INT_MAX], the behavior is undefined. This is explicitly mandated in C++ by Paragraph 5/4:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.

For C, 6.5/5 offers a very similar passage:

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.

So what happens if the value of INT_MIN happens to be less than the negative of INT_MAX (e.g. -32768 and 32767 respectively)? Calculating -(INT_MIN) will be undefined, the same as INT_MAX + 1.

So we need to avoid ever calculating a value that may isn't in the range of [INT_MIN, INT_MAX]. Lucky, INT_MAX + INT_MIN is always in that range, as INT_MAX is a strictly positive value and INT_MIN a strictly negative value. Hence INT_MIN < INT_MAX + INT_MIN < INT_MAX.

Now we can check, whether, INT_MAX + INT_MIN is equal to, less than, or greater than 0.

`INT_MAX + INT_MIN`  |  value of -INT_MIN    | value of -INT_MAX 
------------------------------------------------------------------
         < 0         |  undefined            | -INT_MAX
         = 0         |  INT_MAX = -INT_MIN   | -INT_MAX = INT_MIN
         > 0         |  cannot occur according to 6.2.6.2. of the C standard

Hence, to determine the minimum of INT_MAX and -INT_MIN (in the mathematical sense), the following code is sufficient:

if ( INT_MAX + INT_MIN == 0 )
{
    return INT_MAX; // or -INT_MIN, it doesn't matter
}
else if ( INT_MAX + INT_MIN < 0 )
{
    return INT_MAX; // INT_MAX is smaller, -INT_MIN cannot be represented.
}
else // ( INT_MAX + INT_MIN > 0 )
{
    return -INT_MIN; // -INT_MIN is actually smaller than INT_MAX, may not occur in a conforming implementation.
}

Or, to simplify:

return (INT_MAX + INT_MIN <= 0) ? INT_MAX : -INT_MIN;

The values in a ternary operator will only be evaluated if necessary. Hence, -INT_MIN is either left unevaluated (therefore cannot produce UB), or is a well-defined value.

Or, if you want an assertion:

assert(INT_MAX + INT_MIN <= 0);
return INT_MAX;

Or, if you want that at compile time:

static_assert(INT_MAX + INT_MIN <= 0, "non-conforming implementation");
return INT_MAX;

Getting integer operations right (i.e. if correctness matters)

If you're interested in safe integer arithmetic, have a look at my implementation of safe integer operations. If you want to see the patterns (rather than this lengthy text output) on which operations fail and which succeed, choose this demo.

Depending on the architecture, there may be other options to ensure correctness, such as gcc's option -ftrapv.

stefan
  • 10,215
  • 4
  • 49
  • 90
  • 1
    Note that the unevaluated _const-expr_ `-INT-MIN` has been specifically allowed since C++14. – MSalters Apr 22 '15 at 22:34
  • ... and thus the code needn't compile on pre-C++14 implementations. I can't remember whether or not a diagnostic is required. – Steve Jessop Apr 23 '15 at 00:17
  • Actually Std C. 6.2.6.2 and its equivalent in C++ do constrain the representation so that INT_MAX must be 2^N-1 for some N and INT_MIN must be either -INT_MAX or -INT_MAX-1. – rici Apr 23 '15 at 03:01
  • Could the `if` be replaced with an `#if` ? Or does that run into issues around the preprocessor's integer representation not necessarily being the same as the compiler's? – Harry Johnston Apr 23 '15 at 06:05
9
INT_MAX + INT_MIN < 0 ? INT_MAX : -INT_MIN

Edited to add explanation: Of course the difficulty is that -INT_MIN or abs(INT_MIN) will be undefined if -INT_MIN is too big to fit in an int. So we need some way of checking whether this is the case. The condition INT_MAX + INT_MIN < 0 tests whether -INT_MIN is greater than INT_MAX. If it is, then INT_MAX is the smaller of the two absolute values. If not, then INT_MAX is the larger of the two absolute values, and -INT_MIN is the correct answer.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • 2
    Actually, it's still not correct because `-INT_MIN` can't be represented in an `int` – kdopen Apr 22 '15 at 20:49
  • 7
    @kdopen The branch with `-INT_MIN` is not evaluated then, which means UB doesn't happen. – milleniumbug Apr 22 '15 at 21:06
  • `INT_MAX + INT_MIN` will evaluate to either 0 (sign and magnitude, one’s complement) or -1 (two’s complement). In either case, the result have the value of `INT_MAX`. – chux - Reinstate Monica Apr 22 '15 at 22:03
  • @chux See the answer by JeremyRoman: pre-C99, other representations were allowed. – Brian Bi Apr 22 '15 at 22:04
  • Let me know of a C89 compliant platform where `INT_MAX + INT_MIN` does not have the values 0 nor -1 so I may award a bonus of +50 for this answer. – chux - Reinstate Monica Apr 22 '15 at 22:18
  • Even simpler would just be to compare `INT_MIN < -INIT_MAX` – Jens Gustedt Apr 22 '15 at 23:16
  • 3
    @JensGustedt That makes an analogous assumption, that `-INT_MAX` is representable as an `int`. – Brian Bi Apr 22 '15 at 23:26
  • @Brian On what implementation (real or hypothetical) is `-INT_MAX` not representable? – Gilles 'SO- stop being evil' Apr 22 '15 at 23:59
  • @milleniumbug Actually I reassert my original objection. Although the the branch with {{-INT_MIN}} in it won't be *executed*, it must still be *compiled*. Unless the compiler uses a larger data type (assumes {{sizeof(int) < sizeof(long)}}) to store the constant and then coerces it at run time, this should raise a compilation error on older C compilers. – kdopen Apr 23 '15 at 20:53
  • @kdopen I see no reason why this should not compile. The compiler is not required to evaluate constant expressions at compile time. It may do so, but not if this would cause it to reject a conforming program. This expression follows all the rules. In C, a minus sign is not considered part of an integer constant. – Brian Bi Apr 23 '15 at 21:08
7

In C99 and above, INT_MAX.

Quoth the spec:

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

(Section 6.2.6.2 of http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf)

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
Jeremy Roman
  • 16,137
  • 1
  • 43
  • 44
  • Is that true in C89? That old but not dead language doesn't mandate one of those three representations. – Gilles 'SO- stop being evil' Apr 22 '15 at 20:55
  • Ah, thanks. I don't have a C89 spec at hand, but as you edited, this is true in C99. – Jeremy Roman Apr 22 '15 at 20:58
  • How does this answer the question? – haccks Apr 22 '15 at 21:02
  • 4
    haccks: On C99 and above, the representation restriction prevents abs(INT_MIN) from being smaller than INT_MAX. – Jeremy Roman Apr 22 '15 at 21:12
  • I know and OP does know it too. He is asking about how to find min of mathematical absolute value of `INT_MIN` and `INT_MAX`. – haccks Apr 22 '15 at 21:26
  • @Gilles C89 didn't specifically say 2sC 1sC S&M, but it did require "pure binary" except for the "highest" bit, which was obviously intended to mean the sign bit for signed but as written also formally allowed some limited weirdness in unsigned. AFAIK no machine used any signed representation satisfying "pure binary except sign" other than those three, and no implementor objected to this "clarification" in C99. – dave_thompson_085 Apr 22 '15 at 23:56
  • 2
    haccks: If abs(INT_MIN) >= INT_MAX (which you've agreed to), then min(INT_MAX, abs(INT_MIN)) == INT_MAX. – Jeremy Roman Apr 23 '15 at 00:29
1

On most systems, abs (INT_MIN) is not defined. For example, on typical 32 bit machines, INT_MAX = 2^31 - 1, INT_MIN = - 2^31, and abs (INT_MIN) cannot be 2^31.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • 2
    This is not what the question asks. `abs(INT_MIN)` isn't portable — so how do you compute the absolute value of `INT_MIN`? If `INT_MAX` is -2^31, then abs(`INT_MAX`) *is* 2^31 — this is simple math. That number cannot be represented as an `int32_t`, so you need another way to compare it with `INT_MAX`. – Gilles 'SO- stop being evil' Apr 22 '15 at 20:48
1

-INT_MAX is representable as an int in all C and C++ dialects, as far as I know. Therefore:

-INT_MAX <= INT_MIN ? -INT_MIN : INT_MAX
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
-1

abs(INT_MIN) will invoke undefined behavior. Standard says

7.22.6.1 The abs, labs and llabs functions:

The abs, labs, and llabs functions compute the absolute value of an integer j. If the result cannot be represented, the behavior is undefined.

Try this instead :
Convert INT_MIN to unsignrd int. Since -ve numbers can't be represented as an unsigned int, INT_MAX will be converted to UINT_MAX + 1 + INT_MIN.

#include <stdio.h>
#include <stdlib.h>

unsigned min(unsigned a, unsigned b)
{
    return a < b ? a : b;
}

int main(void)
{
    printf("%u\n", min(INT_MAX, INT_MIN));
}  
Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 1
    This is no bad answer! –  Apr 22 '15 at 20:48
  • I didnt -1, I was just pointing out an error in an earlier revision. I will remove that now – Vality Apr 22 '15 at 20:50
  • 1
    Well it's still incorrect. `abs(INT_MIN)` _may_ be equal to INT_MAX, depending on the architecture. In that case it's clearly well-formed. – stefan Apr 22 '15 at 20:51
  • @stefan; Standard says its UB and that's it. – haccks Apr 22 '15 at 20:51
  • @haccks No, it does not. it does not force 2s or 1s complement. – stefan Apr 22 '15 at 20:52
  • @milleniumbug; Now it answers . – haccks Apr 22 '15 at 20:58
  • @haccks I posted my own answer as I was tired of explaining the same thing over and over again. Please tell me if there are things left unclear. – stefan Apr 22 '15 at 22:11
  • If UINT_MAX == INT_MAX (where INT_MAX == math_not_C_pow(2,num_magnitude_bits)-1) as is allowed but rare, and INT_MIN == -INT_MAX -1 as is allowed and common (for 2sC), b=(unsigned)(INT_MIN) yields 0 and is selected as the minimum, which is not the correct result. – dave_thompson_085 Apr 22 '15 at 23:54