1

有以下类型的三个变量

 uint64_t old_addr, new_addr;
 int delta;

我想做这个任务

 new_addr = old_addr + delta;

然而问题是,当old_addr=915256和时delta=-6472064new_addr成为18446744069414584325

要解决这个问题,我必须检查一些事情:

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

有没有更好更有效的方法?

4

3 回答 3

4

这称为饱和加法,一些处理器为此具有特殊的机器指令。您可以将该代码提取到内联函数中,并根据目标执行环境使用机器指令。

而不是abs(delta)你可以简单地写-delta,因为你已经知道了delta < 0

于 2012-03-09T08:47:58.243 回答
2

问题是什么价值观old_addrnew_addr可以采取什么。以及它们为何如此uint64_t,而不是简单地int。最简单的表达方式是:

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

,但如果old_addr可以大于INT_MAX,这将不起作用。否则,C/C++ 中混合有符号/无符号算术的规则是,使用显式ifs 可能是最安全的,并且在确定值之前不会冒险进行任何混合算术。

请注意,在大多数机器上,如果等于,abs( delta )则仍然是负数 。要正确处理所有情况,您需要以下内容:deltaINT_MIN

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;
}

(就在我的脑海中。那里很容易出现一个错误。)

于 2012-03-09T09:11:51.313 回答
0

这段代码非常简单,并且处理了两个方向的溢出。

#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;
}
于 2012-03-09T14:45:13.863 回答