Краткое содержание Ваш код не работает даже в Debug - он дает неверный результат даже при выходе из цикла.Вы должны знать ограничения арифметики с плавающей точкой.
Если вы пошагово просматриваете свой код с помощью отладчика, вы быстро видите, что не так.
Math.Exp(rate)
велико.Очень большой.Может быть больше, чем число с двойной точностью.Поэтому rnd
начинается со значения Infinity
.
Когда вы переходите к rnd -= term
, это Infinity
минус некоторое число, которое все еще равно Infinity
.Поэтому rnd > 0
всегда верно, поскольку Infinity
больше нуля.
Это продолжается до тех пор, пока term
также не достигнет Infinity
.Тогда rnd -= term
становится Infinity - Infinity
, что составляет NaN
.Все, что сравнивается с NaN
, является ложным, поэтому rnd > 0
внезапно становится ложным, и ваш цикл завершается.
Я не знаю, почему это меняется в режиме выпуска (я не могу воспроизвести его), но этоВполне возможно, что порядок ваших операций с плавающей точкой был изменен.Это может сильно повлиять на результат, если вы имеете дело как с большими, так и с маленькими числами одновременно.Например, term *= rate; term /= ++i
может быть упорядочен так, что term * rate
всегда происходит первым в Debug, и это умножение достигает Infinity
, прежде чем произойдет деление.В Release это может быть переупорядочено таким образом, что rate / ++i
происходит первым, и это останавливает вас от попадания Infinity
.Поскольку вы начали с ошибки, что rnd
всегда Infinity
, ваш цикл может прерваться, только если term
также Infinity
.
Я подозреваю, что это может зависеть от таких факторов, как ваш процессор,а также.
РЕДАКТИРОВАТЬ: См. Этот ответ @HansPassant для гораздо лучшего объяснения.
Итак, еще раз, в то время как разница между зависанием ине зависание может зависеть от Debug vs Release, ваш код никогда не работал в первую очередь.Даже в Debug он дает неправильный результат!
Если вы имеете дело с большими или маленькими числами, вам нужно быть осторожным с ограничениями двойной точности.Числа с плавающей точкой - сложные звери, и имеют много тонкого поведения.Вы должны знать об этом, см., Например, эту знаменитую статью: Что должен знать каждый учёный-компьютерщик об арифметике с плавающей точкой .Помните об ограничениях, проблемах с объединением больших и малых чисел и т. Д.
Если вы работаете вблизи пределов с плавающей запятой, вам необходимо проверить свои предположения: убедитесь, что вы ненапример, иметь дело с числами, которые слишком велики или слишком малы.Если вы ожидаете, что возведение в степень будет меньше Infinity
, проверьте это.Если вы ожидаете, что цикл завершится, добавьте условие защиты, чтобы убедиться, что оно завершается с ошибкой после определенного числа итераций. Проверьте свой код! Убедитесь, что он правильно работает в крайних случаях.
Кроме того, при необходимости используйте библиотеку больших чисел.Если возможно, переделайте свой алгоритм, чтобы он был более дружественным к компьютеру.Многие алгоритмы написаны так, что математики могут изящно писать в учебнике, но их непрактично выполнять процессору.Часто есть версии, которые делают то же самое, но более удобны для компьютера.
Я бы не стал добавлять оператор if или break в производственный код;этот код должен быть как можно более быстрым.)
Не бойтесь одного оператора if в цикле.Если он всегда дает один и тот же результат - например, ваш разрыв никогда не срабатывает - предсказатель ветвления очень быстро срабатывает, и ветвление почти не требует затрат.Это цикл с веткой, который непредсказуем, с которым нужно быть осторожным.