У меня есть следующий (очень простой) код:
#include <cstdint>
#include <iostream>
#include <vector>
int main(void)
{
std::vector<int64_t> result;
int64_t seed = 9564738543572813LL;
int64_t fact = 18465439963655325LL;
for (int i = 0; i < 100 ; i++)
{
seed = seed * fact ;
result.push_back(seed);
}
std::cout << result[0] << ", " << result[1] << std::endl ;
}
Если я скомпилирую его и запущу, все в порядке.
g++ a.cpp -o a
./a
3551237700689479225, 6924873214268169461
Но если я попрошу оптимизировать -O3, результаты будут странными!
g++ -O3 a.cpp -o a
./a
9223372036854775807, 9223372036854775807
Что я делаю не так? Я использую g++ 11.4.0 в Ubuntu 22.04.
Добавьте опцию компилятора -fwrapv
, если (как и я) вы хотите определить поведение для знакового переполнения.
Когда код ломается при оптимизации, это почти всегда ваша вина :)
Что я делаю не так?
Переполнение знакового умножения не определено в языке программирования C. У GCC также есть документация по этому поводу https://www.gnu.org/software/c-intro-and-ref/manual/html_node/Signed-Overflow.html . Неизвестно, каким должен быть результат вашего кода — это может быть что угодно. См. также Неопределённое, неопределённое и определяемое реализацией поведение
Если вы думаете, что хотели это сделать:
seed = (int64_t)((uint64_t)seed * (uint64_t)fact);
или короче, то же самое:
seed = (uint64_t)seed * fact;
Рассмотрите возможность использования дезинфицирующих средств, анализаторов и линтеров для вашего кода. Использование gcc -fsanitize=undefined
позволит обнаружить ошибки, например, в вашем коде.
Переполнение целого числа со знаком — неопределенное поведение, любой вывод вашего кода одинаково действителен.