Question

There are three variables with the following types

 uint64_t old_addr, new_addr;
 int delta;

and I want to do this assignment

 new_addr = old_addr + delta;

However the problem is, when old_addr=915256 and delta=-6472064, the new_addr becoms 18446744069414584325

to fix that I have to check some things:

 if ( delta < 0 ) {
if ( old_addr < abs(delta) )
   new_addr = 0;
    else   
       new_addr = old_addr + delta;
 }

Is there a better and efficient way?

Was it helpful?

Solution

The question is what values old_addr and new_addr can take. And why they are uint64_t, rather than simply int. The simplest expression would be:

new_addr = old_addr + std::min( delta, -static_cast<int>( old_addr ) );

, but if old_addr can be greater than INT_MAX, this won't work. Otherwise, the rules of mixed signed/unsigned arithmetic in C/C++ are such that you're probably safest using explicit ifs, and not risking any mixed arithmetic before being sure of the values.

And note that on most machines, abs( delta ) will still be negative if delta is equal to INT_MIN. To correctly handle all of the cases, you'ld need something like:

if ( delta > 0 ) {
    new_addr = std::numeric_limits<uin64_t>::max() - delta > old_addr
            ?   old_addr + delta
            :   std::numeric_limits<uint64_t>::max();
} else if ( delta < 0 ) {
    new_addr = old_addr != 0 && -(delta + 1) < old_addr - 1
            ?   old_addr + delta
            :   0;
} else {
    new_addr = old_addr;
}

(Just off the top of my head. There could easily be an off by one error in there.)

OTHER TIPS

This is called saturated addition, and some processors have special machine instructions for that. You could extract that code into an inline function, and depending on the target execution environment, use the machine instruction.

Instead of abs(delta) you can simply write -delta, since you already know that delta < 0.

This code is very simple and takes care of overflow in both directions.

#include <assert.h>
#include <inttypes.h>
#include <stdint.h>

static uint64_t saturated_add(uint64_t a, int delta) {
  uint64_t result = a + delta;
  if (delta < 0 && result > a) {
    return 0;
  } else if (delta > 0 && result < a) {
    return -1;
  } else {
    return result;
  }
}

int main() {
  assert(saturated_add(915256, -6472064) == 0);
  assert(saturated_add(100, -99) == 1);
  assert(saturated_add(100, -100) == 0);
  assert(saturated_add(100, -101) == 0);
  assert(saturated_add(UINT64_C(0x1111222233334444), -0x33334445) == UINT64_C(0x11112221FFFFFFFF));
  assert(saturated_add(-5, 6) == UINT64_C(-1));
  assert(saturated_add(-5, 5) == UINT64_C(-1));
  assert(saturated_add(-5, 4) == UINT64_C(-1));
  assert(saturated_add(-5, 3) == UINT64_C(-2));
  return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top