I would like to compute the absolute difference of two integers. Naively, this is just abs(a - b). However, this has a couple of problems, depending on whether the integers are signed or unsigned:
-
For unsigned integers,
a - bwill be a large positive number ifbis larger thana, and the absolute value operation will not fix that. -
For signed integers,
a - bmay be outside of the range of values that can be represented as a signed integer, thus leading to undefined behavior. (Obviously, this means that the result will need to be an unsigned integer.)
How can these problems be addressed in an efficient way?
I would like an algorithm (or pair of algorithms) that works for both signed and unsigned integers, and does not rely on casting the values to a larger integer size.
(Also, to clarify: my question is not how to write this in code, but what exactly I should be writing in order to guarantee overflow-safety. Computing abs(a - b) as a signed value and then casting to an unsigned value doesn’t work, because signed integer overflow is typically an undefined operation, at least in C.)
(I’ve been working this out on my own after asking the question — I thought it would be harder, and I’d still welcome other answers if there are better ones!)
The solution for unsigned integers is relatively straightforward (as described in Jack Toole’s answer), and works by moving the (implied) conditional outside the subtraction so that we are always subtracting the smaller number from the larger one, and are not comparing a potentially-wrapped value to zero:
This just leaves the question of signed integers. Consider the following variation:
We can easily prove that this works by using modulo arithmetic. We know that
aand(unsigned) aare congruent moduloUINT_MAX. Further, the unsigned-integer subtraction operation is congruent to actual subtraction moduloUINT_MAX, so combining these facts, we know that(unsigned) a - (unsigned) bis congruent to the actual value ofa - bmoduloUINT_MAX. Finally, because we know that the actual value ofa - bmust be between0andUINT_MAX-1, we know that this is an exact equality.