Как обнаружить переполнение при вычитании двух знаковых 32-битных чисел в C? - PullRequest
1 голос
/ 27 октября 2009

У меня есть два целых числа со знаком, и я хотел бы вычесть их. Мне нужно знать, переполнено ли оно.

int one;
int two;
int result = two-one;

if (OVERFLOW) {
    printf("overflow");
} else {
    printf("no overflow");
}

Нечто подобное. Есть ли хороший способ сделать это?

Ответы [ 3 ]

10 голосов
/ 28 октября 2009

Вам нужно отловить оверлей (или недополнение) до того, как произойдет. Как только это произойдет, вы окажетесь на неопределенном поведении на земле, и все ставки сняты.

#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;
}
6 голосов
/ 27 октября 2009

Во-первых, переполнение в подписанных вычислениях приводит к неопределенному поведению в C.

Во-вторых, забыв про UB на секунду и придерживаясь типичного поведения переполнения машины дополнения 2: переполнение проявляется в том, что результат «движется» в «неправильном направлении» от первого операнда, т.е. заканчивается больше, чем первый операнд с положительным вторым операндом (или меньше, чем первый операнд с отрицательным вторым операндом).

В вашем случае

int one, two;

int result = two - one;
if ((result < two) != (one > 0))
  printf("overflow");
1 голос
/ 27 октября 2009

Вы можете сделать это с большей точностью и сравнить. Скажем, у вас есть 32-разрядные целые числа. Вы можете преобразовать их в 64-разрядные целые числа, вычесть, затем сравнить полученный результат с самим собой приведения к 32-разрядному, а затем снова до 64-разрядных.

Я бы так не поступил с int, потому что язык не дает вам гарантий на размеры ... Может быть int32_t и int64_t от <inttypes.h> (от C99).

Если вы используете Windows, можете использовать <a href="http://msdn.microsoft.com/en-us/library/bb776671(VS.85).aspx" rel="nofollow noreferrer">ULongSub()</a> и т. Д., Что возвращает код ошибки при переполнении.

...