Как обнаружить переполнение при вычитании двух знаковых 32-битных чисел в C?
-
06-07-2019 - |
Вопрос
У меня есть два целых числа со знаком, и я хотел бы вычесть их. Мне нужно знать, переполнено ли оно.
int one;
int two;
int result = two-one;
if (OVERFLOW) {
printf("overflow");
} else {
printf("no overflow");
}
Нечто подобное. Есть ли хороший способ сделать это?
Решение
Во-первых, переполнение в подписанных вычислениях вызывает неопределенное поведение в C.
Во-вторых, на секунду забываем об UB и придерживаемся типичного поведения переполнения на машине дополнения 2: переполнение проявляется в том, что результат " Move " в & "неверное направление &"; из первого операнда, т.е. когда результат заканчивается больше, чем первый операнд с положительным вторым операндом (или меньше, чем первый операнд с отрицательным вторым операндом).
В вашем случае
int one, two;
int result = two - one;
if ((result < two) != (one > 0))
printf("overflow");
Другие советы
Вам необходимо уловить переполнение (или недостаточное) до того, как это произойдет. Как только это произойдет, вы окажетесь на неопределенном поведении , и все ставки отменены.
#include <limits.h>
#include <stdio.h>
int sum_invokes_UB(int a, int b) {
int ub = 0;
if ((b < 0) && (a < INT_MIN - b)) ub = 1;
if ((b > 0) && (a > INT_MAX - b)) ub = 1;
return ub;
}
int main(void) {
printf("(INT_MAX-10) + 8: %d\n", sum_invokes_UB(INT_MAX - 10, 8));
printf("(INT_MAX-10) + 100: %d\n", sum_invokes_UB(INT_MAX - 10, 100));
printf("(INT_MAX-10) + INT_MIN: %d\n", sum_invokes_UB(INT_MAX - 10, INT_MIN));
printf("100 + INT_MIN: %d\n", sum_invokes_UB(100, INT_MIN));
printf("-100 + INT_MIN: %d\n", sum_invokes_UB(-100, INT_MIN));
printf("INT_MIN - 100: %d\n", sum_invokes_UB(INT_MIN, -100));
return 0;
}
Вы можете сделать это с большей точностью и сравнить. Скажем, у вас есть 32-разрядные целые числа. Вы можете преобразовать их в 64-разрядные целые числа, вычесть, затем сравнить полученный результат с самим собой приведения к 32-разрядному, а затем снова до 64-разрядных.
Я бы не стал так поступать с int
, потому что язык не дает вам гарантий на размеры ... Может быть, int32_t
и int64_t
из <inttypes.h>
(из C99).
Если вы используете Windows, можете использовать ULongSub()
и т. д., который возвращает код ошибки при переполнении.