Подтвердить что ты не робот

Переполнение появляется даже при использовании unsigned long int

Когда я делаю следующий расчет:

unsigned long long int data_size = 60123456 * 128 * sizeof(double);
printf("data_size= %llu  \n", data_size);

Я неожиданно получаю предупреждение о переполнении:

test.c:20:49: warning: overflow in expression; result is -894132224 with type 'int' [-Winteger-overflow]
    unsigned long long int data_size = 60123456 * 128 * sizeof(double);
                                                ^
1 warning generated.

Я не могу понять, почему эта ошибка появляется, хотя я использую unsigned long long int! Может кто-нибудь объяснить, почему? Спасибо вам

4b9b3361

Ответ 1

Переполнение происходит до того, как значение присваивается переменной. Чтобы избежать использования длинного длинного суффикса:

60123456LL * 128 * sizeof(double)

(комментарии также предлагают ULL, который здесь не нужен, потому что значения находятся в пределах диапазона подписки, но это будет общий путь)

Ответ 2

Вы слишком поздно конвертируете в "long long". 60123456 * 128 все еще является переполняющим int-выражением.

Попробуйте 60123456LL * 128. (LL, потому что результирующее значение не гарантируется в соответствии с длинным.)

Ответ 3

unsigned long long int data_size = 60123456 * 128 * sizeof(double);  // trouble-some code

Тип адресата unsigned long long int data_size = не имеет отношения к расчету продукта 60123456 * 128 * sizeof(double).

Лучше всего застраховать математику, используя, по крайней мере, размер целевого типа, чтобы избежать переполнения. В случае OP это означает константу с LLU.


Существует 2 вычисления продукта, каждый со своей собственной математикой.

60123456 - это int или long в зависимости от диапазона int. Предположим, что это int.

60123456 * 128 является int * int. Математический продукт 7695802368 превосходит 32-разрядный целочисленный целочисленный диапазон, таким образом со знаком целочисленного переполнения или undefined поведение (UB) с 32-разрядным int.

Если 60123456 * 128 не переполнялся, говорит 64-бит int, то следующее умножение будет * sizeof(double);, и поэтому int * size_t приводит к типу продукта size_t.

Вычисление продукта должно использовать как минимум unsigned long long математику, как показано ниже:

unsigned long long int data_size = 1LLU * 60123456 * 128 * sizeof(double);

// or
unsigned long long int data_size = 60123456LLU * 128 * sizeof(double);

// or 
unsigned long long int data_size = 60123456;
data_size *= 128 * sizeof(double);

// or avoiding naked magic numbers
#define A_HEIGHT 60123456
#define A_WIDTH 128
unsigned long long int data_size = 1LLU * A_HEIGHT * A_WIDTH * sizeof(double);

sizeof (double) указывает, что код пытается найти размер некоторой 2D-подобной структуры. Я бы предположил, что код выглядит следующим образом. Обратите внимание, что тип результата sizeof равен size_t, поэтому математика продукта выполняется с использованием не менее size_t math.

size_t data_size = sizeof(double) * 60123456 * 128;
printf("data_size= %zu\n", data_size);

См. также Зачем писать 1000 000 000 как 1000 * 1000 * 1000 в C? и мой ответ причины не использовать 1000 * 1000 * 1000 для соответствующих сведений..

Ответ 4

Константы 60123456 и 128 имеют тип int, поэтому выражение 60123456 * 128 также имеет тип int. Значение этого выражения переполнило бы диапазон int. Компилятор может обнаружить его в этом случае, потому что оба операнда являются константами.

Вы должны использовать суффикс ULL в первом операнде, чтобы сделать тип константы unsigned long long. Таким образом, он соответствует назначению типа. Затем для любой операции, связанной с этим значением, другой операнд будет продвигаться до unsigned long long до того, как будет применена операция, и у вас не будет переполнения.

Итак, получившееся выражение должно выглядеть так:

unsigned long long int data_size = 60123456ULL * 128 * sizeof(double);

Ответ 5

Лексер присоединяет типы к константам во время фазы перевода 7 (преобразование токенов предварительной обработки в токены C). cf ISO9899 5.1.1.2 Translation phases. Как говорили другие, лексер будет прикреплять к вашим константам тип int вместо unsigned long long, как вам угодно, и для того, чтобы default arithmetic conversions сгенерировал константу типа unsigned long long в правой части задания вам нужно сказать лексеру, чтобы прикрепить тип unsigned long long по крайней мере к одной константе из операции *, которая генерирует переполнение, поэтому записывая 60123456ULL * 128 или 60123456 * 128ULL или и то, и другое.