What's the most efficient way to perform bounded addition?

140 Views Asked by At

Given an unsigned integer x and a signed integer dx, what's the most efficient way to perform x = x + dx while preventing over/underflow? I could do something like:

unsigned int x;
int dx;

...

long int result = (long int)(x + dx);
if (result < 0) {
    x = 0;
} else if (result > UINT_MAX) {
    x = UINT_MAX;
} else {
    x = result;
}

But I'm wondering if there's a better way.

EDIT: updated the example to be semantically correct.

2

There are 2 best solutions below

0
0___________ On

The only efficient way is to use inline assembly (add and check the processor flags which are not accessible in C) or to use compiler builtins.

Example:

int add(int a,  int b)
{
    int result;
    return __builtin_sadd_overflow(a,b,&result) ? result : INT_MAX;
}

unsigned uadd(unsigned a,  int b)
{
    unsigned result;
    return __builtin_uadd_overflow(a,b,&result) ? result : UINT_MAX;
}

and the resulting machine code:

add:
        add     edi, esi
        jo      .L3
        mov     eax, 2147483647
        ret
.L3:
        mov     eax, edi
        ret



uadd:
        add     edi, esi
        jc      .L9
        mov     eax, -1
        ret
.L9:
        mov     eax, edi
        ret
0
chux - Reinstate Monica On

OP's approach fails at inception as x + dx does not benefit with extended integer width - the addition still uses unsigned math. Casting to long after the addition is moot.

// Not useful
long int result = (long int)(x + dx);
// Perhaps
long int result = x + (long int)dx;
// or
long long result = x + (long long)dx;

Using long addition provides no wider math when int and long have the save size. Perhaps long long? Even long long is not specified to be wider than int, yet it often.


What's the most efficient way to perform bounded addition?

I'll set aside most efficient and focus on correct portable functionality for all unsigned int x, int dx.

A variation on @unwind:

unsigned saturated_add_alt(unsigned int x, int dx) {
  if (dx >= 0) {
    if (x > UINT_MAX - dx) {
      return UINT_MAX;
    }
  } else if (x < (1 - dx)) { // Avoid x < -dx.  UB when dx == INT_MIN
    return 0;
  }
  return x + dx;
}