如何在 C 中安全地添加和限制有符号整数?

How can I safely add and clamp a signed integer in C?

我有一个 signed int 类型的值,它总是被限制在例如 ±1073741824 (2^30) 之间。我应该能够安全地添加或减去任何任意值,如果 sum/difference 在范围之外,它是 clamped (任何高于最大值的值都映射到最大值,并且任何低于最小值的值都映射到最小值)。我有以下内容:

signed int Min(signed int X, signed int Y) {
    return X < Y ? X : Y;
}
signed int Max(signed int X, signed int Y) {
    return X > Y ? X : Y;
}
signed int X;
void UpdateX(signed int Delta) {
    X = Min(Max(X+Delta, -(1<<30)), (1<<30));
}

但是,如果 Delta and/or X 足够大或足够小以至于值上溢或下溢,这将调用 Undefined Behavior,因为在钳位之前存在 signed int 上溢。一种解决方案是临时使用更大的整数类型,但对于已经使用最大可用类型的其他情况,这不是一个选项。我如何安全地添加然后钳制 signed int 而不会冒调用未定义行为的风险?

您担心溢出是对的。检查溢出的明显技术是在溢出发生后 检测到它,如果涉及任何未定义的行为,这当然为时已晚。

标准技术是重新安排测试。而不是说:

if(X + Delta > MAX) { whoops! it overflowed; }

从两边减去 Delta 得到

if(X > MAX - Delta) { whoops! it overflowed; }

当然你也要考虑Delta是否定的可能性。所以在你的情况下,我相信这样的事情会做到这一点:

#define MAX (1<<30)

void UpdateX(signed int Delta) {
    if(Delta >= 0) X = (X <=  MAX - Delta) ? X + Delta :  MAX;
    else           X = (X >= -MAX - Delta) ? X + Delta : -MAX;
}

另见 How to check for signed integer overflow in C without undefined behaviour?

另见 Question 20.6b in the C FAQ list