我有一个signed int
类型的值,它总是被夹在例如±1073741824(2^30(之间。我应该能够安全地添加或减去任何任意值,如果和/差在范围之外,则被钳制(任何高于最大值的值都映射到最大值,任何低于最小值的值映射到最小值(。我有以下内容:
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和/或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;
}
另请参阅如何在没有未定义行为的情况下检查C中的有符号整数溢出?
另请参见C常见问题列表中的问题20.6b。