Разрешение целочисленных переполнений со знаком в C / C ++ - PullRequest
7 голосов
/ 22 ноября 2010

I хочет, чтобы целые числа со знаком переполнялись, когда они становились слишком большими. Как мне этого достичь, не используя следующий по величине тип данных (или когда я уже на int128_t)?

Например, при использовании 8-битных целых чисел 19 * 12 обычно равно 260, но я хочу получить результат 1 11 10 01 00 с обрезанным 9-м битом, то есть -27.

Ответы [ 7 ]

6 голосов
/ 22 ноября 2010

Переполнение со знаком не определено в C, а это для реального .

Одно из следующих решений:

signed_result = (unsigned int)one_argument + (unsigned int)other_argument;

Приведенное выше решение включает в себя поведение, определяемое реализацией вокончательное преобразование из unsigned в int, но не вызывать неопределенное поведение.Для большинства платформ компиляции, определяемых реализацией, результатом будет именно тот результат, который вы ожидаете получить.

Наконец, оптимизирующий компилятор для одной из многочисленных платформ, на которых выбор, определяемый реализацией, вынуждает компилятор дать вам ожидаемое поведение скомпилирует приведенный выше код в очевидную инструкцию сборки.

В качестве альтернативы, если вы используете gcc, то параметры -fwrapv / -fno-strict-overflow могут быть именно тем, что вы хотите.Они предоставляют дополнительную гарантию в отношении стандарта, на который распространяются подписанные переполнения.Я не уверен в разнице между ними.

3 голосов
/ 22 ноября 2010

Переполнение со знаком целого числа не определено в соответствии со стандартами C и C ++.Невозможно выполнить то, что вы хотите, без конкретной платформы.

0 голосов
/ 22 ноября 2010

Это можно сделать правильным стандартным способом C, если у вас есть доступ к типу unsigned такой же ширины, как ваш тип signed (то есть имеет еще один бит значения),Для демонстрации с int64_t:

int64_t mult_wrap_2scomp(int64_t a, int64_t b)
{
    uint64_t result = (uint64_t)a * (uint64_t)b;

    if (result > INT64_MAX)
        return (int64_t)(result - INT64_MAX - 1) - INT64_MAX - 1;
    else
        return (int64_t)result;
}

Это не приводит к каким-либо проблемным промежуточным результатам.

0 голосов
/ 22 ноября 2010

Предполагается, что двоичная целочисленная арифметика с двумя дополнениями (что в наши дни является разумным допущением) для сложения и вычитания, просто приведена к unsigned для выполнения вычисления. Для умножения и деления убедитесь, что операнды положительны, приведены к беззнаковым, вычислите и скорректируйте знаки.

0 голосов
/ 22 ноября 2010

Звучит так, как будто вы хотите сделать незапятнанную целочисленную арифметику, а затем сложить результат в целое число со знаком:

unsigned char a = 19;
unsigned char b = 12;

signed char c = (signed char)(a*b);

должно дать вам то, что вы ищете. Дайте нам знать, если это не так.

0 голосов
/ 22 ноября 2010

Используйте большие типы данных. С GMP у вас будет все необходимое место.

0 голосов
/ 22 ноября 2010

Вы можете создать объективную оболочку для int, но это потребует большого количества служебного кода.

...