Ответы с форумов MSDN

Точность значений с плавающей точкой

Date: 04.05.2021 18:03:31

>Интересно что в начале 90х когда я был студентом (причем не по компьютерной специальности), об этих (и других - их не мало) особенностях рассказывали в курсе по вычислительной технике. Очевидно сейчас это тайна. :)

Изучается и сейчас, даже в средней школе на информатике, насколько я помню. Проблема в том, что само знание представления с плавающей точкой еще не дает навыка избегать подобных багов, это приходит только с опытом реального применения их к расчетам, причем баги эти могут быть самыми разными. Я например правило "сравнивать float нужно с точностью до дельты" усвоил сразу, но все равно срезался на том, что пытался в цикле делать x+=0.01 и ожидал, что результат будет кратен 0.01. Я не думаю, что можно требовать знать это от всех пользователей, работающих с компьютером. Более реальное решение в том, чтобы эти проблемы до пользователей не доходили. Стандартные float и double быстрые, но неудобные для всего, кроме научных расчетов. Использование decimal, где это возможно, будет хорошим шагом вперед. В Excel, кстати, 1.6 * 10 равно 16 с точностью до 30 знаков после запятой, видимо, у него своя система вещественных чисел. 

>Я проверяю не на равенство! А проблема в том, что проверяемое условие дает True в качестве результата, но при этом выполнятся блок, который должен выполнятся при False!

Отладчик использует для вычисления выражений свой интерпретатор IL, который может иметь другие флаги параметров float по сравнению с живым процессом с CLR.

Message 34

Date: 05.05.2021 16:54:21

>Скорее всего вы видите округление при выводе. 

Да, похоже, Excel даже если включить показ 30 знаков после запятой, все равно показывает округленное значение. Поэтому проблему можно воспроизвести только на более сложных случаях. 

>Decimal имеет весьма небольшой диапазон и тоже подвержен тому же эффекту. Просто проблема проявляется на другом наборе чисел и привыкшие к десятичной системе люди ее не замечают. :)

Это так, но Decimal позволяет избавиться от потери копеек при вычислениях денежных сумм в различных финансовых приложениях, а большой диапазон там часто не требуется. Поэтому в бизнес приложениях Decimal >> Double.

>Другой интересный вопрос - что происходит при сравнении в конкретном языке? Целое преобразуется в float/double и потом сравнивается? Или же float/double преобразуется в целое и сравниваются целые значения? Честно говоря я не знаю как это делает VB. Надо смотреть IL который был сгенерирован. 

Судя по IL, преобразуется во float и сравнивается:

  IL_001e:  ldloc.0
  IL_001f:  ldc.r4     10.
  IL_0024:  mul
  IL_0025:  ldloc.1
  IL_0026:  conv.r4
  IL_0027:  cgt

где local 0 - float32 _maxP, local 1 - int32 _selectedP

>мой вопрос состоял в том, что почему ветвление в If происходит не по результату полученному в теле If, а как тут преобразуются числа совершенно неважно! А персонаж, начинает тут нести бред, не относящийся к теме заданного вопроса.

Рассуждения по поводу чисел с плавающей точкой здесь вполне относятся к теме вопроса, так как в рассматриваемой программе именно они и происходят. Это публичный форум, где каждый может высказаться, индивидуальной помощи никто не обещал. Почему результат If неверен я уже выдвинул одну гипотезу, что дело в разном поведении вычислителя выражений в отладчике и реальной программы. Подтвердить на практике не получилось. В один момент вроде удалось поймать, когда значения были разными, но после перезапуска студии они вновь оказались одинаковыми. Возможно, здесь задействован какой-то глобальный параметр, который влияет на точность вычислений, и зависит от многих факторов. Но есть одна тонкость, ведь 1.6*10 = 16, а это степень 2, соответственно оно должно быть точно представимо в IEEE Float! Почему точность здесь вообще влияет? Словом, проблема загадочная, и точного ответа пока дать нельзя...

Message 33

Date: 06.05.2021 8:46:01

Ура, мне удалось поймать проблему. Проблема воспроизводится только на платформе x64, и в C# и в VB. Собственно вот, значения в отладчике и в реальной программе различаются:

float debugger issue

Как видно, это на 100% проблема точности вычислений с float. При x64 вычисления с float имеют такую же точность, как и double, и это сравнение может быть выполнено без потери точности. Но Visual Studio, в которой выполняется вычислитель выражений, всегда является 32-битным процессом. Поэтому в ней данная операция выполняется как есть, и приводит к потере точности. Проблема в таком виде проявляется в VS2019, а в VS2012 она проявляется наоборот, значения различаются на платформе x86, и в отладчике точность не теряется.  


Автор: VadimTagil

Главная страница - Список тем - Репозиторий на GitHub