给定一个无符号整数x
和一个有符号整数dx
,在防止溢出/下溢的同时执行x = x + dx
的最有效方法是什么?我可以做一些类似的事情:
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;
}
但我想知道是否有更好的方法。
EDIT:将示例更新为语义正确。
唯一有效的方法是使用内联汇编(添加并检查在C中不可访问的处理器标志(或使用编译器内置。
示例:
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;
}
以及由此产生的机器代码:
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
OP的方法在一开始就失败了,因为x + dx
没有从扩展的整数宽度中受益——加法仍然使用unsigned math
。添加后铸造到long
是没有实际意义的。
// 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;
当int
和long
具有存储大小时,使用long
加法不会提供更宽的数学。也许long long
?即使long long
也没有被指定为比int
宽,但它经常如此。
执行有界加法的最有效方法是什么?
我将留出最高效的,并专注于所有unsigned int x, int dx
的正确可移植功能。
@unflex:的变体
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;
}