У меня когда-то была очень тонкая ошибка в одном из проектов, которые мне пришлось поддерживать. По существу, это делалось примерно так:Практические последствия нарушения строгой сглаживания между int и float
union Value {
int64_t int64;
int32_t int32[2];
int16_t shorts[4];
int8_t chars[8];
float floats[2];
double float64;
};
Value v;
// in one place (not sure about exact code, it could be just memcpy):
v.shorts[0] = <some short value>;
v.shorts[1] = <some other short value>;
// in another place:
float f = v.floats[0];
Теперь, что касается стандарта, это просто UB. На практике это может означать что угодно, но я вряд ли смогу представить себе разумную реализацию, которая привела бы к тому, что код, описанный выше, начнет Третью мировую войну или дезинтегрирует мой компьютер. В реальной жизни, я могу только представить себе две вещи, случающиеся:
Компилятор может ввернуть что-то с оптимизацией, не понимая, что он имеет дело с той же памятью здесь. Довольно маловероятно в этом случае, поскольку записи и чтения происходят в совершенно разных местах.
Ничего плохого на самом деле не происходит, и значение
float
просто читается поэтапно.
На практике это было почти всегда случай 2, за один раз, за исключением. После запуска программы, скомпилированной с MSVC 2010 в режиме выпуска около 100-150 входных файлов, в одном из файлов она породила неверное значение, которое отличалось ровно одним битом от того, что должно было быть в соответствии с здравым смыслом. Это тоже был довольно значительный бит, так что вместо, скажем, 1.5
, я получил что-то вроде 117.9
. Я смог проследить его до этого точного чтения, и после исправления кода, чтобы придерживаться строгих правил псевдонимов, все работало нормально.
Вопрос теперь, чисто с точки зрения низкого уровня, что могло бы вызвать это? Некоторые особенности обработки ЦП с плавающей запятой? Особенности кеширования оборудования? Компилятор причуды? Почему только одно значение было неправильным?
Аппаратное обеспечение было некоторым старым двухъядерным 64-разрядным процессором Intel с 32-разрядной Windows 7, если это поможет. Программа представляет собой однопоточное консольное приложение, ничего необычного. Проблема была на 100% воспроизводимой, одни и те же входные файлы всегда производили один и тот же вывод, и это всегда одно и то же значение, которое было неправильным.
Вам действительно нужно посмотреть на сборку; рассуждение о UB на уровне языка - безумие, рассуждение об этом на уровне сборки просто глупо;) – TartanLlama
@TartanLlama, это было давно, у меня больше нет точного кода и ввода, поэтому я не думаю, что смогу воспроизведите его. Я больше искал теоретические знания от экспертов по программному и аппаратного обеспечения низкого уровня. Не то, что * вызвало * это, но то, что * могло * вызвало это. –
Я предполагаю, что компилятор, который выполнял много анализа псевдонимов, мог заметить, что часть 'floats' этого объединения никогда не была инициализирована, поэтому объект не жив и поэтому просто дает вам что-то неинициализированное. LLVM имеет 'undef' для обозначения этого в IR, например. – TartanLlama