Переполнение целочисленного деления

Проблема

Я думал о переполнении integer (type int), и мне кажется, что разделение может переполниться.

пример: на моей текущей платформе, у меня есть

INT_MIN == -INT_MAX - 1

и так

INT_MIN < -INT_MAX

и так

INT_MIN / -1 > -INT_MAX / -1

и так

INT_MIN / -1 > INT_MAX.

следовательно, деление (INT_MIN / -1 ) переполняется.


Вопросы

Итак, у меня два вопросы:

  1. какой (кросс-платформенный) код C можно написать, чтобы предотвратить переполнение разделения (для типа (подписанного) int)?

  2. какие гарантии (в стандарте C или C++) могут помочь в разработке кода?


например, если стандарт гарантирует, что у нас есть либо

INT_MIN == -INT_MAX - 1

или

INT_MIN == -INT_MAX,

затем появляется следующий код для предотвращения переполнений.

#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;
}

2 ответов


какие гарантии (в стандарте C или C++) могут помочь в разработке кода?

C определяет целочисленное представление со знаком как использование 1 из 3 форм: знак и величина, дополнение двух или дополнение единиц. Учитывая эти формы, только деление на 0 и два дополняют деление INT_MIN/-1 может переполниться.

какой (кросс-платформенный) код C можно написать, чтобы предотвратить переполнение разделения (для типа (подпись) 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;
}

1) как и для любой другой операции в C, приложение должно гарантировать, что:

  • тип, используемый для самого расчета, достаточно велик, и
  • тип переменной, в которой хранится результат, достаточно велик.

способ обеспечить это-установить ограничения размера каждого операнда перед операцией. Какие пределы подходят, зависит от алгоритма и назначения переменных.

2) Если вы используете stdint.ч из стандарта C вы получаете гарантии того, насколько велики переменные, переносимо. Вы никогда не должны использовать int при написании переносимого кода.

Что касается случая написания безопасной процедуры деления, она будет принимать 32-битные целые числа в качестве параметров, а затем выполнять расчет на 64-битных целых числах и возвращать результат как 32-битное целое число.

#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;
}

Если требуется дополнительная информация о том, почему разделение не удалось, замените bool перечислением.