#c #integer-arithmetic #unsigned-long-long-int
#c #целое число-арифметика #unsigned-long-long-int
Вопрос:
Арифметический результат для одного и того же выражения приводит к разным результатам в зависимости от того, определяю ли я целое число в одной строке или использую несколько шагов:
int main() {
unsigned long long veryBigIntOneLine = ((255*256 255)*256 255)*256 255;
unsigned long long veryBigInt = 255;
veryBigInt *= 256;
veryBigInt = 255;
veryBigInt *= 256;
veryBigInt = 255;
veryBigInt *= 256;
veryBigInt = 255;
unsigned long long veryBigIntParanthesis = (((((255*256) 255)*256) 255)*256) 255;
unsigned long long fourthInt = 256;
fourthInt *= 256;
fourthInt *= 256;
fourthInt *= 256;
--fourthInt;
cout << "veryBigIntOneLine: " << veryBigIntOneLine << endl;
cout << "veryBigInt: " << veryBigInt << endl;
cout << "veryBigIntParanthesis: " << veryBigIntParanthesis << endl;
cout << "fourthInt: " << fourthInt << endl;
return 0;
}
все они должны описывать одно и то же число, 256 ^ 4-1 (или 2 ^ 32-1), но результат отличается.
veryBigIntOneLine: 18446744073709551615
veryBigInt: 4294967295
veryBigIntParanthesis: 18446744073709551615
fourthInt: 4294967295
4294967295 — ожидаемый ответ (поскольку он дается для всех четырех выражений калькулятором Google).
Также 18446744073709551615, вероятно, не является точным результатом того, что вычисляется, поскольку я получаю предупреждение о переполнении во время компиляции для обоих однострочных выражений (даже когда я пробовал с типом __int128). На самом деле это 2 ^ 64-1, что является максимальным значением для unsigned long long с моим компилятором (veryBigIntOneLine 1 дает 0).
Комментарии:
1. Кроме того, самыми простыми способами получения максимального значения для
unsigned long long
было бы(-1ULL)
или(~0ULL)
.
Ответ №1:
Код инициализации ((255*256 255)*256 255)*256 255
страдает от переполнения целого числа со знаком, которое является неопределенным поведением, а также от неявного преобразования signed int в unsigned . В то время как пошаговые вычисления позволяют избежать этих проблем, поскольку правый операнд неявно преобразуется в беззнаковый long long .
Простое использование соответствующих литералов устранит эти проблемы:
unsigned long long veryBigIntOneLine{ ((255ull*256ull 255ull)*256ull 255ull)*256ull 255ull}; // 4294967295
Комментарии:
1. Хорошо, спасибо,
unsigned long long veryBigIntOneLine = (unsigned long long)((255*256 255)*256 255)*256 255;
работает нормально.2. И большинство компиляторов предупредят об этом, если будет предложено. Хотя добавление
constexpr
изменяет это на ошибку, которая всегда будет диагностироваться: coliru.stacked-crooked.com/a/5cc7d4a84abd756a Кроме того, было бы достаточно сделать одну из внутренних констант anunsigned long long
, а для остальных — зависеть от неявных поощрений.3. @JulienDhondt Вы можете просто работать везде с unsigned long long, используя соответствующий буквенный суффикс. Также обратите внимание на использование инициализации списка — это запрещает сужающее преобразование.
4. @Deduplicator Итак, в принципе, это
unsigned long long veryBigIntOneLine = ((255ull*256 255)*256 255)*256 255;
должно работать?5. @JulienDhondt Вы также можете определить свои собственные суффиксы. См. Определяемый пользователем литерал
Ответ №2:
Это потому, что вы не используете unsigned long long
литералы. Если вы хотите, чтобы литералы соответствовали вашим определениям, вам необходимо использовать:
255ULL 256ULL * 255ULL ...
Это ULL
очень важно, если вы создаете числа, состоящие из 64 бит. В C без суффикса число может быть 64, 32 или даже всего 16 бит (даже байты на некоторых CRAY, где 64 бита. Это также означает, что ваш код работал бы просто find в одной из этих систем CRAY.)
Комментарии:
1. Спасибо, единственным известным мне буквальным спецификатором был L» для строк wchar_t[]. Я не знал, что другие литеральные выражения были напечатаны, обработаны / вычислены и только после преобразования при определении / присвоении.
2. Примечание:
int
не обязательно 32-разрядный (и существует довольно много архитектур, имеющих 16-разрядный int!).