Я написал поучительный пример для коллеги, чтобы показать ему, почему тестирование поплавков на равенство часто является плохой идеей. В примере, который я использовал, было добавление 0,1 в десять раз и сравнение с 1,0 (тот, который мне показали во вводном числовом классе). Я был удивлен, обнаружив, что два результата были равны ( код + вывод ).
float @float = 0.0f;
for(int @int = 0; @int < 10; @int += 1)
{
@float += 0.1f;
}
Console.WriteLine(@float == 1.0f);
Некоторые исследования показали, что на этот результат нельзя полагаться (так же, как и на равенство с плавающей точкой). Больше всего меня удивило то, что добавление кода после другого кода могло изменить результат вычисления ( code + output ). Обратите внимание, что этот пример имеет точно такой же код и 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 обнаруживает, что в дальнейшем ей потребуется блокировать число с плавающей запятой.
Я немного искал, но не смог найти причину такого поведения. Кто-нибудь может подсказать мне?