Как C# оценивает плавающую точку в парении и непосредственном окне по сравнению со скомпилированным?

Я вижу, что что-то нечетное с хранением удваивается в словаре и перепутано относительно почему.

Вот код:

            Dictionary a = new Dictionary();
            a.Add("a", 1e-3);

            if (1.0 < a["a"] * 1e3)
                Console.WriteLine("Wrong");

            if (1.0 < 1e-3 * 1e3)
                Console.WriteLine("Wrong");

Второе, если оператор работает как ожидалось; 1.0 не меньше чем 1,0. Теперь, первое, если оператор оценивает как верный. Очень нечетная вещь состоит в том, что, когда я нависаю над, если, intellisense говорит мне ложь, уже код счастливо перемещается в Консоль. WriteLine.

Это - для C# 3.5 в Visual Studio 2008.

Действительно ли это - проблема точности с плавающей точкой? Затем, почему делает второе если работа оператора? Я чувствую, что пропускаю что-то очень фундаментальное здесь.

Любое понимание ценится.

Edit2 (Повторно имеющий целью вопрос немного):

Я могу принять математическую проблему точности, но мой вопрос теперь: почему делает парение, оценивают правильно? Это также верно для непосредственного окна. Я вставляю код сначала, если оператор в непосредственное окно и он оценивает ложь.

Обновление

В первую очередь, большое спасибо за все большие ответы.

У меня также есть проблемы при воссоздании этого в другом проекте на той же машине. Смотря на настройки проекта, я не вижу различий. Смотря на IL между проектами, я не вижу различий. Смотря на дизассемблирование, я не вижу очевидных различий (помимо адресов памяти). Все же, когда я отлаживаю исходный проект, я вижу: screenshot of problem

Непосредственное окно говорит мне, если ложь, все же код попадает в условное выражение.

Во всяком случае лучший ответ, я думаю, должен подготовиться к арифметике с плавающей точкой в этих ситуациях. Причина я не мог позволить этому движению, больше имеет отношение к вычислениям отладчика, отличающимся от времени выполнения. Так благодарит очень много Brian Gideon и stephentyrone для некоторых очень проницательных комментариев.

7
задан Sergey 30 August 2019 в 18:14
поделиться

4 ответа

Это проблема с плавающей точностью.

Второй оператор работает, потому что компилятор считает выражение 1e-3 * 1e3 перед выдачей .exe.

Посмотрите его в ILDasm / Reflector, он выдаст что-то вроде

 if (1.0 < 1.0)
                Console.WriteLine("Wrong");
13
ответ дан 6 December 2019 в 08:44
поделиться

Проблема здесь довольно тонкая. Компилятор C # не (всегда) генерирует код, который выполняет вычисления с двойной точностью, даже если это тот тип, который вы указали. В частности, он генерирует код, который выполняет вычисления с «расширенной» точностью с использованием инструкций x87, без округления промежуточных результатов до удвоения.

В зависимости от того, оценивается ли 1e-3 как двойное или длинное двойное, и вычисляется ли умножение в double или long double можно получить любой из следующих трех результатов:

  • (long double) 1e-3 * 1e3, вычисленное в long double, равно 1.0 - epsilon
  • (double) вычислено 1e-3 * 1e3 in double равно 1.0
  • (double) 1e-3 * 1e3, вычисленное в long double, равно 1.0 + epsilon

Очевидно, первое сравнение, тот, который не оправдывает ваши ожидания, оценивается в порядке, описанном в третьем сценарии, который я перечислил. 1e-3 округляется до удвоения либо потому, что вы сохраняете его и загружаете снова, что вызывает округление, либо потому, что C # распознает 1e-3 как литерал с двойной точностью и обрабатывает его таким образом. Умножение оценивается как long double, потому что C # имеет безумную числовую модель , именно так компилятор генерирует код.

Умножение во втором сравнении либо оценивается с использованием одного из два других метода (вы можете выяснить, какой из них, попробовав «1> 1e-3 * 1e3»), или компилятор округляет результат умножения, прежде чем сравнивать его с 1.0, когда он оценивает выражение во время компиляции.

Вероятно, вы можете указать компилятору не использовать расширенную точность без вашего указания через некоторые настройки сборки; включение генерации кода для SSE2 также может работать.

4
ответ дан 6 December 2019 в 08:44
поделиться

См. Ответы здесь

2
ответ дан 6 December 2019 в 08:44
поделиться

Ммм ... странно. Я не могу воспроизвести вашу проблему. Я также использую C # 3.5 и Visual Studio 2008. Я набрал в вашем примере точно , как он был опубликован, и я не вижу выполнения ни одного оператора Console.WriteLine .

Кроме того, второй оператор if оптимизируется с помощью компилятор. Когда я исследую как отладочную, так и выпускную сборки в ILDASM / Reflector, я не вижу никаких свидетельств этого. Это происходит потому, что я получаю предупреждение компилятора о том, что на нем обнаружен недостижимый код.

Наконец, я не понимаю, как это могло быть проблемой точности с плавающей запятой. Почему компилятор C # статически оценивает два двойных значения иначе, чем CLR во время выполнения? Если бы это было действительно так, то можно было бы привести аргумент, что в компиляторе C # есть ошибка.

Изменить: Поразмыслив еще немного, я еще больше убедился, что это не проблема точности с плавающей запятой. Вы, должно быть, наткнулись на ошибку в компиляторе или отладчике, или опубликованный вами код не совсем соответствует вашему действительному запущенному коду. Я очень скептически отношусь к ошибке в компиляторе, но ошибка в отладчике кажется более вероятной. Попробуйте пересобрать проект и запустить его снова. Возможно, отладочная информация, скомпилированная вместе с exe, рассинхронизировалась или что-то в этом роде.

Я очень скептически отношусь к ошибке в компиляторе, но ошибка в отладчике кажется более вероятной. Попробуйте пересобрать проект и запустить его снова. Возможно, отладочная информация, скомпилированная вместе с exe, рассинхронизировалась или что-то в этом роде.

Я очень скептически отношусь к ошибке в компиляторе, но ошибка в отладчике кажется более вероятной. Попробуйте пересобрать проект и запустить его снова. Возможно, отладочная информация, скомпилированная вместе с exe, рассинхронизировалась или что-то в этом роде.

2
ответ дан 6 December 2019 в 08:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: