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

Любопытная арифметическая ошибка - 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

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

4b9b3361

Ответ 1

 255*256*256*256

все операнды int вы переполнены int. Переполнение знакового целого является undefined поведением в C и С++.

EDIT:

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

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

Ответ 2

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

Ответ 3

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

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

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

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

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

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

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

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

Теперь, когда вы назначаете -16777216 переменной uint64_t, она преобразуется обратно в unsigned as-if, добавляя 2 64. (В отличие от преобразования без знака в подпись эта семантика предписывается стандартом.) Это изменяет битовый шаблон, устанавливая все высокие 32 бита числа в 1 вместо 0, как вы ожидали:

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

И если вы напишете 0xFFFFFFFFFF000000 в десятичной форме, вы получите 18446744073692774400.

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

Ответ 4

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

Ответ 5

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