'How can I safely add and clamp a signed integer in C?

I have a value, of type signed int, which is always clamped between, for example, ±1073741824 (2^30). I should be able to safely add or subtract any arbitrary value, and if the sum/difference is outside the range it is clamped (any value above the maximum maps to the maximum, and any value below the minimum maps to the minimum). I have the following:

signed int Min(signed int X, signed int Y) {
    return X < Y ? X : Y;
}
signed int Max(signed int X, signed int Y) {
    return X > Y ? X : Y;
}
signed int X;
void UpdateX(signed int Delta) {
    X = Min(Max(X+Delta, -(1<<30)), (1<<30));
}

However, if Delta and/or X is sufficiently large or small that the value overflows or underflows, this invokes Undefined Behavior because there is a signed int overflow before the clamping. One solution is to use a larger integer type temporarily, but this is not an option for other cases where the largest available type is already being used. How can I safely add then clamp a signed int without risking invoking undefined behavior?



Solution 1:[1]

You're right to worry about overflow. The obvious techniques for checking for overflow detect it after it's happened, which is of course too late, if there's any undefined behavior involved.

The standard technique is to rearrange the test. Rather than saying:

if(X + Delta > MAX) { whoops! it overflowed; }

subtract Delta from both sides so you have

if(X > MAX - Delta) { whoops! it overflowed; }

Of course you also have to consider the possibility that Delta is negative. So in your case I believe something like this will do it:

#define MAX (1<<30)

void UpdateX(signed int Delta) {
    if(Delta >= 0) X = (X <=  MAX - Delta) ? X + Delta :  MAX;
    else           X = (X >= -MAX - Delta) ? X + Delta : -MAX;
}

See also How to check for signed integer overflow in C without undefined behaviour?

See also Question 20.6b in the C FAQ list.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1