Любопытная арифметическая ошибка — 255x256x256x256 = 18446744073692774400

Я столкнулся со странной вещью, когда программировал под с ++. Это о простом умножении.

Код:

unsigned __int64 a1 = 255*256*256*256;
unsigned __int64 a2= 255 << 24; // same as the above

cerr()<<"a1 is:"<<a1;
cerr()<<"a2 is:"<<a2;

Интересно, что результат:

a1 is: 18446744073692774400
a2 is: 18446744073692774400

тогда как должно быть: (используя калькулятор подтверждает)

4278190080

Кто-нибудь может сказать мне, как это возможно?

25

Решение

 255*256*256*256

все операнды int вы переполнены int, Переполнение целого числа со знаком — неопределенное поведение в C и C ++.

РЕДАКТИРОВАТЬ:

обратите внимание, что выражение 255 << 24 в вашем втором объявлении также вызывает неопределенное поведение, если ваш int тип 32-bit, 255 x (2^24) является 4278190080 который не может быть представлен в 32-bit int (максимальное значение обычно 2147483647 на 32-bit int в двух дополнительных представлениях).

C и C ++ оба говорят за E1 << E2 что если E1 имеет подписанный тип и положительный и что E1 x (2^E2) не может быть представлен в виде E1программа вызывает неопределенное поведение. Вот ^ это математическое мощность оператор.

39

Другие решения

Ваши литералы int, Это означает, что все операции фактически выполняются на intи быстро переполнить. Это переполненное значение при преобразовании в 64-разрядное целое число без знака является значением, которое вы наблюдаете.

17

Возможно, стоит объяснить, что случилось с производством числа 18446744073692774400. Технически говоря, написанные вами выражения вызывают «неопределенное поведение», и поэтому компилятор мог сгенерировать что-нибудь в результате; однако, предполагая, int это 32-битный тип, которым он почти всегда является сейчас, вы получите тот же «неправильный» ответ, если напишите

uint64_t x = (int) (255u*256u*256u*256u);

и это выражение делает не вызвать неопределенное поведение. (Преобразование из unsigned int в int включает в себя от реализации поведение, но так как никто не создавал CPU с единичным дополнением или величиной знака и величины в течение многих лет, все реализации, с которыми вы, вероятно, столкнетесь, определяют его точно так же.) Я написал приведение в стиле C, потому что все, что я представляю сказанное здесь в равной степени относится к C и C ++.

Прежде всего, давайте посмотрим на умножение. Я пишу правую часть в шестнадцатеричном виде, потому что так легче увидеть, что происходит.

255u * 256u               = 0x0000FF00u
255u * 256u * 256u        = 0x00FF0000u
255u * 256u * 256u * 256u = 0xFF000000u (= 4278190080)

Этот последний результат, 0xFF000000u, имеет старший бит из установленного 32-битного числа. Приведение этого значения к подписанный 32-битный тип, следовательно, становится отрицательным, как будто 232 был вычтен из него (это операция, определяемая реализацией, о которой я упоминал выше).

(int) (255u*256u*256u*256u) = 0xFF000000 = -16777216

Я пишу шестнадцатеричное число там, без u суффикс, чтобы подчеркнуть, что битовый паттерн значение не изменяется при преобразовании его в тип со знаком; это только переосмыслено.

Теперь, когда вы назначаете -16777216 для uint64_t переменная, она обратно преобразуется в без знака как-будто, добавив 264. (В отличие от преобразования без знака в подпись, эта семантика предписана стандартом.) делает измените битовую комбинацию, установив все старшие 32 бита числа в 1 вместо 0, как вы ожидали:

(uint64_t) (int) (255u*256u*256u*256u) = 0xFFFFFFFFFF000000u

И если ты пишешь 0xFFFFFFFFFF000000 в десятичном виде вы получите 18446744073692774400.

В качестве заключительного совета всякий раз, когда вы получаете «невозможное» целое число из C или C ++, попробуйте распечатать его в шестнадцатеричном формате; гораздо проще увидеть странности арифметики фиксированной ширины с двумя дополнениями.

16

Ответ прост — переполнен.

0

Здесь переполнение произошло в int, и когда вы присваиваете его неподписанному int64, оно преобразуется в 18446744073692774400 вместо 4278190080

0
По вопросам рекламы [email protected]