问题
我一直在考虑整数(int类型)溢出,我突然想到除法可能溢出。
示例:在我当前的平台上,我有
INT_MIN == -INT_MAX - 1
和
INT_MIN < -INT_MAX
和
INT_MIN / -1 > -INT_MAX / -1
和
INT_MIN / -1 > INT_MAX.
因此除法(INT_MIN/-1)会溢出。
我有两个问题:
可以写什么(跨平台)C代码来防止除法溢出(对于类型(signed) int)?
什么保证(在C或c++标准中)可能有助于设计代码?
例如,如果标准保证有
INT_MIN == -INT_MAX - 1
或
INT_MIN == -INT_MAX,
则出现以下代码以防止溢出。
#include <limits.h>
/*
Try to divide integer op1 by op2.
Return
0 (success) or
1 (possibly overflow prevented).
In case of success, write the quotient to res.
*/
int safe_int_div(int * res, int op1, int op2) {
/* assert(res != NULL); */
/* assert(op2 != 0); */
if ( op1 == INT_MIN && op2 == -1 ) {
return 1;
}
*res = op1 / op2;
return 0;
}
什么保证(在C或c++标准中)可能有助于设计代码?
C将有符号整数表示为使用3种形式中的一种:符号和大小、二补数或一补数。在这些形式下,INT_MIN/-1
的除0和2的补除可能溢出。
Update: 2023: C23预计只支持两个互补。
为了防止除法溢出(对于类型(signed) int),可以编写哪些(跨平台)C代码?
int safe_int_div(int * res, int op1, int op2) {
if (op2 == 0) {
return 1;
}
// 2's complement detection
#if (INT_MIN != -INT_MAX)
if (op1 == INT_MIN && op2 == -1) {
return 1;
}
#endif
*res = op1 / op2;
return 0;
}
1)就像C语言中的任何其他操作一样,应用程序必须确保:
- 用于计算本身的类型足够大,并且
- 存储结果的变量类型足够大。
确保这一点的方法是在操作前设置每个操作数的大小限制。合适的限制取决于算法和变量的目的。
2)如果您使用C标准的stint .h,您可以保证变量有多大,可移植。在编写可移植代码时,永远不要使用int
。
对于编写安全除法例程的情况,它将32位整数作为参数,然后对64位整数进行计算,并返回32位整数。
#include <stdint.h>
#include <stdbool.h>
/*
Try to divide integer op1 by op2.
Return
true (success) or
false (possibly overflow prevented).
In case of success, write the quotient to res.
In case of failure, res remains untouched.
*/
bool safe_int_div (int32_t* res, int32_t op1, int32_t op2) {
if(op2 == 0)
return false;
int64_t res64 = (int64_t)op1 / (int64_t)op2;
if(res64 > INT32_MAX || res64 < INT32_MIN)
return false;
*res = (int32_t)res64_t;
return true;
}
如果需要进一步了解除法失败的原因,请将bool替换为enum。