Оптимизация CLR JIT нарушает причинно-следственную связь?
Я написал поучительный пример для коллеги, чтобы показать ему, почему тестирование поплавков на равенство часто является плохой идеей. В примере, который я использовал, было добавление 0,1 в десять раз и сравнение с 1,0 (тот, который мне показали в моем вводном числовом классе). Я был удивлен, обнаружив, что два результатабыли равны (код + вывод).
float @float = 0.0f;
for(int @int = 0; @int < 10; @int += 1)
{
@float += 0.1f;
}
Console.WriteLine(@float == 1.0f);
Некоторые исследования показали, что на этот результат нельзя полагаться (так же, как и на равенство с плавающей точкой). Больше всего меня удивило то, что добавление кодапосле другой код может изменить результат расчета (код + вывод). Обратите внимание, что этот пример имеет точно такой же код и IL, с добавлением еще одной строки C #.
float @float = 0.0f;
for(int @int = 0; @int < 10; @int += 1)
{
@float += 0.1f;
}
Console.WriteLine(@float == 1.0f);
Console.WriteLine(@float.ToString("G9"));
Я знаю, что я не должен использовать равенство на поплавках и, следовательно, не должен беспокоиться об этом, но я нахожу это довольно удивительным, как и для всех, кому я это показал. Делать вещипосле Вы выполнили расчет, изменяет значение предыдущего расчета? Я не думаю, что это модель вычислений, которую люди обычно имеют в виду.
Я не совсем озадачен, кажется безопасным предположить, что в «равном» случае происходит какая-то оптимизация, которая изменяет результат вычисления (сборка в режиме отладки предотвращает «равный» случай). По-видимому, оптимизация прекращается, когда CLR обнаруживает, что в дальнейшем ему потребуется блокировать число с плавающей запятой.
Я немного искал, но не смог найти причину такого поведения. Кто-нибудь может подсказать мне?