Как проверить на целочисленное переполнение со знаком в C без неопределенного поведения?
Есть (1):
// assume x,y are non-negative
if(x > max - y) error;
И (2):
// assume x,y are non-negative
int sum = x + y;
if(sum < x || sum < y) error;
Какой из них предпочтительнее или есть лучший способ.
3 ответа
Целочисленное переполнение является каноническим примером "неопределенного поведения" в C (отмечая, что операции над целыми числами без знака никогда не переполняются, вместо этого они определены для обхода). Это означает, что, как только вы выполнили x + y
Если он переполнен, вы уже из шланга. Слишком поздно делать какие-либо проверки - ваша программа могла уже потерпеть крах. Думайте об этом, как о проверке деления на ноль - если вы ждете, пока разделение будет выполнено, проверить, уже слишком поздно.
Таким образом, это означает, что метод (1) является единственным правильным способом сделать это. За max
, ты можешь использовать INT_MAX
от <limits.h>
,
Если x
и / или y
может быть отрицательным, тогда все будет сложнее - вам нужно выполнить тест таким образом, чтобы сам тест не мог вызвать переполнение.
if ((y > 0 && x > INT_MAX - y) ||
(y < 0 && x < INT_MIN - y))
{
/* Oh no, overflow */
}
else
{
sum = x + y;
}
Вы можете проверить только переполнение с помощью unsigned
целые и арифметические:
unsigned a,b,c;
a = b + c;
if (a < b) {
/* overflow */
}
Поведение переполнения целыми числами со знаком не определено в C, но на большинстве машин вы можете использовать
int a,b,c;
a = b + c;
if (c < 0 ? a > b : a < b) {
/* overflow */
}
Это не будет работать на машинах, которые используют какую-либо насыщающую арифметику
Вам нужно только проверить один из них. Если x + y переполнится, оно будет меньше, чем x и y. Следовательно:
int sum = x + y;
if (sum < x) error;
должно быть достаточно.
На следующем сайте есть много вещей о целочисленном переполнении:
Если вы хотите обрабатывать отрицательные числа, его можно расширить:
int sum = x + y;
if (y >= 0) {
if (sum < x) error;
} else {
if (sum > x) error;
}