Как обнаружить переполнение при вычитании двух знаковых 32-битных чисел в C?

StackOverflow https://stackoverflow.com/questions/1633561

  •  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() и т. д., который возвращает код ошибки при переполнении.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top