NaN обрабатывается по-разному в разных версиях g++
Рассмотрим следующую программу, которая явно глючит:
#include <cstdio>
double test(int n) {
if (n % 2 == 0)
return 0.0;
// warning: control reaches end of non-void function
}
int main() {
printf("%.9lf\n", test(0));
printf("%.9lf\n", test(1));
printf("%.9lf\n", test(2));
printf("%.9lf\n", test(3));
return 0;
}
При компиляции с g++ версии 4.2.4 (Ubuntu 4.2.4-1ubuntu4) на 32-битной Ubuntu 8.04 он выдает следующий вывод:
0.000000000
nan
0.000000000
nan
При компиляции с g++ версии 4.4.3 (Ubuntu 4.4.3-4ubuntu5) на 64-битной Ubuntu 10.04 он выдает следующий вывод:
0.000000000
0.000000000
0.000000000
0.000000000
Кажется, что старый компилятор выполняет некоторую дополнительную работу, чтобы вернуть NaN вместо мусора, в то время как новый компилятор просто возвращает все, что есть в памяти. Что именно вызывает эту разницу в поведении и как ее контролировать и сделать ее предсказуемой в разных версиях компилятора?
РЕДАКТИРОВАТЬ: Извините, что не упомянул неопределенное поведение ранее. Я знаю, что разница заключается в том, что эта программа имеет неопределенное поведение. Что я хотел бы знать, почему предыдущая версия gcc, кажется, прилагает определенные усилия и создает код, который последовательно возвращает NaN, и когда это поведение изменилось на то, которое наблюдалось в последней версии gcc. Кроме того, под "предсказуемым" я подразумевал не то, как писать хорошие программы на C++, а то, как управлять этим поведением gcc (возможно, с некоторыми параметрами командной строки?).
4 ответа
Код, который вы показываете, имеет неопределенное поведение, так как не может вернуть значение в test
со всех путей управления.
Ничего не ожидать, и вы ничего не можете сделать, чтобы контролировать это, кроме как написать правильную программу в первую очередь.
РЕДАКТИРОВАТЬ: я утверждаю, что вы не должны полагаться на то, что компилятор, кажется, делает.
Даже если вы думаете, что должны, вы не должны.
Изучение дизассемблирования покажет, во что именно этот фрагмент кода будет скомпилирован, но вы не сможете обобщить что-либо еще, кроме этого конкретного фрагмента кода, особенно при наличии оптимизаций. Неопределенное поведение на самом деле означает то, что вы думаете, что оно делает: если вы не освоите кодовую базу GCC, не будет никакой гарантии, что то, что вы наблюдаете, будет воспроизводимым в другом контексте.
Единственное разумное решение здесь - это скомпилировать с -Wall и исправить те "не все возвращаемые пути возвращают значение", чтобы поведение было 1) определено, 2) определено вами.
Ваша функция возвращает double
еще когда n % 2 != 0
это ничего не возвращает.
Вы находитесь в стране неопределенного поведения.
Что я хотел бы знать, почему предыдущая версия gcc, кажется, прилагает определенные усилия и создает код, который последовательно возвращает NaN, и когда это поведение изменилось на то, которое наблюдалось в последней версии gcc. Кроме того, под "предсказуемым" я подразумевал не то, как писать хорошие программы на C++, а то, как управлять этим поведением gcc (возможно, с некоторыми параметрами командной строки?).
Поскольку вы находитесь в сфере неопределенного поведения, маловероятно, что есть какая-либо причина. Авторы компилятора (оправданно), как правило, не тратят никаких усилий на то, что делает компилятор в случаях неопределенного поведения. Что бы здесь ни происходило, это почти наверняка возникающее поведение от (по-видимому) несвязанных изменений между версиями компилятора, а не преднамеренный выбор со стороны любого инженера компилятора. Там не будет флаг, чтобы изменить его. Не зависит от этого. Исправьте ваш код.
Игнорируйте предупреждения на свой страх и риск.
if (n % 2 == 0) return 0.0; else return 0.0;
Работает нормально, иначе в половине случаев test()
возвращает мусор.
И неопределенное поведение не зависит от версии, так как я также получаю NaN:
$ gcc --version
gcc (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
$ uname -a
Linux tallguy 2.6.38-8-generic #42-Ubuntu SMP Mon Apr 11 03:31:50 UTC 2011 i686 i686 i386 GNU/Linux