Комментарий Дэвида правильный, но недостаточно сильный. Нет гарантии, что выполнение этого вычисления дважды в одной и той же программе даст одинаковые результаты.
Спецификация C # предельно ясна по этому вопросу:
Операции с плавающей запятой могут выполняться с большей точностью, чем тип результата операции. Например, некоторые аппаратные архитектуры поддерживают «расширенный» или «длинный двойной» тип с плавающей точкой с большей дальностью и точностью, чем тип double, и неявно выполняют все операции с плавающей точкой, используя этот тип с более высокой точностью. Только при чрезмерных затратах на производительность такие аппаратные архитектуры могут быть выполнены для выполнения операций с плавающей запятой с меньшей точностью, и вместо того, чтобы требовать реализации для потери как производительности, так и точности, C # позволяет использовать тип с более высокой точностью для всех операций с плавающей запятой , Помимо предоставления более точных результатов, это редко дает ощутимые результаты. Однако в выражениях вида x * y / z
, где умножение дает результат, выходящий за пределы двойного диапазона, а последующее деление возвращает временный результат обратно в двойной диапазон, тот факт, что выражение оценивается в формате более высокого диапазона может привести к конечному результату вместо бесконечности.
Компилятор C #, джиттер и среда выполнения имеют широкую широту, чтобы дать вам более точные результаты , чем того требует спецификация, в любое время и по прихоти - их не нужно выбирать делать это последовательно, а на самом деле они этого не делают.
Если вам это не нравится, не используйте двоичные числа с плавающей запятой; либо используйте десятичные числа или рациональные рациональные числа.
Я не понимаю, почему приведение к float в методе, который возвращает float, имеет значение, которое он делает
Отличный балл.
Ваша программа-пример демонстрирует, как небольшие изменения могут вызвать большие эффекты. Обратите внимание, что в некоторых версиях среды выполнения приведение к плавающему явно дает иной результат, чем не выполнение. Когда вы явно приводите к плавающей точке, компилятор C # дает подсказку среде выполнения , чтобы сказать «выведите эту вещь из режима сверхвысокой точности, если вы используете эту оптимизацию». Как отмечается в спецификации, имеет потенциальную стоимость производительности.
То, что это происходит с округлением до "правильного ответа", является просто счастливой случайностью; правильный ответ получен, потому что в этом случае потеря точности потеряла его в правильном направлении .
Чем отличается .net 4?
Вы спрашиваете, в чем разница между 3,5 и 4,0 средами выполнения; Различие очевидно, что в 4.0 джиттер выбирает более высокую точность в вашем конкретном случае, а джиттер 3,5 выбирает не делать этого. Это не значит, что эта ситуация была невозможна в 3,5; это было возможно в каждой версии среды исполнения и каждой версии компилятора C #. Вы только что натолкнулись на случай, когда на вашей машине они различаются по своим деталям. Но для джиттера всегда было разрешено выполнять эту оптимизацию, и он всегда делал это по своей прихоти.
Компилятор C # также полностью вправе выбирать аналогичную оптимизацию при вычислении констант с плавающей точкой во время компиляции. Два по-видимому идентичных вычисления в константах могут иметь различные результаты в зависимости от деталей состояния времени выполнения компилятора.
* +1045 * МоВ общем, ваше ожидание, что числа с плавающей запятой должны иметь алгебраические свойства действительных чисел, полностью не соответствует действительности; у них нет этих алгебраических свойств. Операции с плавающей запятой даже не
ассоциативны ; они, конечно, не подчиняются законам мультипликативных инверсий, как вы, вероятно, ожидаете их. Числа с плавающей точкой являются лишь приближением реальной арифметики; приближение, которое достаточно близко, скажем, для моделирования физической системы или вычисления сводной статистики, или чего-то подобного.