问题
The Problem
I have been thinking about integer (type int) overflows, and it occurs to me that division could overflow.
Example: On my current platform, I have
INT_MIN == -INT_MAX - 1
and thus
INT_MIN < -INT_MAX
and thus
INT_MIN / -1 > -INT_MAX / -1
and thus
INT_MIN / -1 > INT_MAX.
Hence, the division ( INT_MIN / -1 ) does overflow.
The Questions
So, I have two questions:
What (cross-platform) C code could one write in order to prevent division overflows (for type (signed) int)?
What guarantees (in C or C++ standard) might help to devise the code?
For example, if the standard guarantees that we have either
INT_MIN == -INT_MAX - 1
or
INT_MIN == -INT_MAX,
then the following code appears to prevent overflows.
#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;
}
回答1:
What guarantees (in C or C++ standard) might help to devise the code?
C specifies signed integer representation as using 1 of 3 forms: sign and magnitude, two’s complement, or ones’ complement. Given these forms, only division by 0 and two’s complement division of INT_MIN/-1
may overflow.
What (cross-platform) C code could one write in order to prevent division overflows (for type (signed) int)?
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;
}
回答2:
1) Just as for any other operation in C, the application has to ensure that:
- the type used for the calculation itself is large enough, and
- the type of the variable where the result is stored is large enough.
The way to ensure this is to set size limits of each operand before the operation. What limits that are suitable depends on the algorithm and the purpose of the variables.
2) If you use stdint.h of the C standard, you get guarantees of how big the variables are, portably. You should never use int
when writing portable code.
As for the case of writing a safe division routine, it would take 32 bit integers as parameters, then perform the calculation on 64 bit integers and return the result as a 32 bit integer.
#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;
}
If further information of why the division failed is desired, replace the bool with an enum.
来源:https://stackoverflow.com/questions/30394086/integer-division-overflows