c语言 - 执行有界加法的最有效方法是什么?



给定一个无符号整数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;

intlong具有存储大小时,使用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;
}

最新更新