I'm trying to detect the overflow when adding a signed offset to an unsigned position
uint32 position;
int32 offset;  // it could be negative
uint32 position = position+offset;
How can I check whether the result is overflow or underflow?
I have thought of an ugly way but not sure of its correctness.
offset < 0 && position + offset >= position
offset > 0 && position + offset <= position
And I'm also wondering if there's a more elegant way to do it.
Update:
What's the best solution if offset is long?
uint32 position;
long offset;  // it could be negative
uint32 position = position+offset;
                Your test(s) is (are) correct. I don't see a more elegant way right now, perhaps there isn't.
Why the conditions are correct: arithmetic on uint32_t is arithmetic modulo 2^32. Conversion from int32_t to uint32_t is normally the reinterpretation of the bit-pattern (in any case, as @caf pointed out, it's reduction modulo 2^32 here, so it definitely works). Regard position and offset as arbitrary precision integers.   Overflow happens if and only ifposition + offset >= 2^32. But offset < 2^31, so position + offset < position + 2^31, which is less than position + 2^32, the next value that reduces to position modulo 2^32, so as uint32_t, then position + offset < position. On the other hand, if offset > 0 and position + offset < position, evidently overflow has occurred. Underflow happens if and only if position + offset < 0 as mathematical integers. Since offset >= -2^31, similar reasoning shows that underflow has occurred if and only if offset < 0 && position + offset > position. 
The following function check for overflow/underflow when adding an int32_t to an uint32_t. It also contains some test cases as evidence of correctness.
#include <stdint.h>
#include <assert.h>
int is_overflow (uint32_t position, int32_t offset)
{
    if (offset > 0 && offset > UINT32_MAX - position) {
        // really we checked (offset + position > UINT32_MAX)
        // overflow
        return 1;
    }
    else if (offset < 0 && (uint32_t)offset <= UINT32_MAX - position) {
        // really we checked  (position + (uint32_t)offset <= UINT32_MAX)
        // the (uint32_t)offset maps negative offset to [2^31, UINT32_MAX]
        // underflow
        return -1;
    }
    // no over/underflow
    return 0;
}
uint32_t abs_of_negative_int32 (int32_t offset)
{
    assert(offset < 0);
    return ((UINT32_MAX - (uint32_t)offset) + 1);
}
int main (int argc, char *argv[])
{
    int r;
    r = is_overflow(0, 0);
    assert(r == 0);
    r = is_overflow(0, 1);
    assert(r == 0);
    r = is_overflow(0, INT32_MAX - 1);
    assert(r == 0);
    r = is_overflow(0, INT32_MAX);
    assert(r == 0);
    r = is_overflow(0, -1);
    assert(r == -1);
    r = is_overflow(0, INT32_MIN + 1);
    assert(r == -1);
    r = is_overflow(0, INT32_MIN);
    assert(r == -1);
    r = is_overflow(UINT32_MAX, 0);
    assert(r == 0);
    r = is_overflow(UINT32_MAX, 1);
    assert(r == 1);
    r = is_overflow(UINT32_MAX - 1, 1);
    assert(r == 0);
    r = is_overflow(UINT32_MAX - 1, 2);
    assert(r == 1);
    r = is_overflow(UINT32_MAX - 1, INT32_MAX);
    assert(r == 1);
    r = is_overflow(UINT32_MAX - INT32_MAX, INT32_MAX);
    assert(r == 0);
    r = is_overflow(UINT32_MAX - INT32_MAX + 1, INT32_MAX);
    assert(r == 1);
    r = is_overflow(abs_of_negative_int32(INT32_MIN), INT32_MIN);
    assert(r == 0);
    r = is_overflow(abs_of_negative_int32(INT32_MIN) - 1, INT32_MIN);
    assert(r == -1);
    return 0;
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With