以下类型有三个变量
uint64_t old_addr, new_addr;
int delta;
我想做这个作业
new_addr = old_addr + delta;
但问题是,当old_addr=915256
和delta=-6472064
时,new_addr
becoms 18446744069414584325
修复我必须检查一些事情:
if ( delta < 0 ) {
if ( old_addr < abs(delta) )
new_addr = 0;
else
new_addr = old_addr + delta;
}
有更好更有效的方法吗?
答案 0 :(得分:4)
这称为饱和加法,有些处理器有特殊的机器指令。您可以将该代码提取到内联函数中,并根据目标执行环境使用机器指令。
您可以简单地写abs(delta)
而不是-delta
,因为您已经知道delta < 0
。
答案 1 :(得分:2)
问题是old_addr
和new_addr
可以采用的值。为什么呢
它们是uint64_t
,而不仅仅是int
。最简单的表达方式
将是:
new_addr = old_addr + std::min( delta, -static_cast<int>( old_addr ) );
,但如果old_addr
可能大于INT_MAX
,则无效。
否则,C / C ++中混合有符号/无符号算术的规则是
这样你可能最安全地使用显式if
,而不是冒险
在确定值之前的任何混合算术。
请注意,在大多数计算机上,如果abs( delta )
仍为负数
delta
等于INT_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;
}
(就在我的头顶上。很容易就会出现一个错误 在那里。)
答案 2 :(得分: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;
}