В фа#. Как эффективно сравнивать поплавки на равенство, которые почти равны? Он должен работать и для очень больших, и для очень маленьких значений. Я думаю сначала сравнить экспоненту, а затем мантиссу (мантисса), игнорируя последние 4 бита из 52 бит. Это хороший подход? Как я могу получить показатель степени и значимость числа с плавающей запятой?
F# — как сравнивать числа с плавающей запятой
Ответы (2)
F# float
— это просто сокращение от System.Double
. В этом случае вы можете использовать BitConverter.DoubleToInt64Bits метод для эффективного (и безопасного!) "преобразования" значения F# float
в int64
; это полезно, потому что позволяет избежать выделения byte[]
, как упомянул Джон в своем комментарии. Вы можете получить показатель степени и мантиссу из этого int64
, используя несколько простых побитовых операций.
Однако, как сказал Джон, вам, вероятно, будет лучше с простой проверкой относительной точности. Вероятно, это будет самое быстрое решение и «достаточно близкое» для многих случаев использования (например, проверка того, сошелся ли итеративный решатель с решением). Если вам нужна конкретная степень точности, взгляните на код NUnit — у него есть несколько хороших API-интерфейсов для подтверждения того, что значения находятся в пределах определенного процента или количества ulps от ожидаемого значения.
Когда вы спрашиваете, как сравнивать значения с плавающей запятой, которые почти равны, вы спрашиваете:
- У меня есть два значения,
x
иy
, которые были вычислены с помощью арифметики с плавающей запятой, поэтому они содержат ошибки округления и являются приближениями к идеальным математическим значениям x и y. Как я могу использоватьx
иy
с плавающей запятой для сравнения математических x и y на равенство?
Здесь есть две проблемы:
- Мы не знаем, сколько ошибок может быть в
x
илиy
. Некоторые комбинации арифметических операций увеличивают количество ошибок, а другие уменьшают их. Ошибки вx
иy
могут варьироваться от нуля до бесконечности, и вы не предоставили нам никакой информации об этом. - Часто предполагается, что цель состоит в том, чтобы получить результат «равно», когда
x
иy
не равны, но близки друг к другу. Это преобразует ложноотрицательные результаты (о неравенстве сообщается, даже если математические x и y равны) в положительные. Однако это создает ложные срабатывания (сообщается о равенстве, даже если математические x и y не равны).
Для этих проблем нет общего решения.
- В целом невозможно узнать, может ли приложение терпеть сообщение о том, что значения равны, когда они должны быть неравными, или наоборот, не зная конкретных подробностей об этом приложении.
- В целом невозможно узнать, сколько ошибок может быть в
x
иy
.
Следовательно, не существует правильного общего критерия равенства значений, рассчитанных приблизительно.
Обратите внимание, что эта проблема на самом деле не связана с проверкой на равенство. Как правило, невозможно вычислить любую функцию неверных данных (за исключением тривиальных функций, таких как функции-константы). Поскольку x
и y
содержат ошибки, невозможно использовать x
для вычисления log(x) без ошибок или для вычисления аркосинуса(y) или sqrt(x ) без ошибок. На самом деле, если ошибки сделали y
чуть больше 1, а y
нет, или сделали x
немного меньше нуля, а x нет, то вычисление acos(y)
или sqrt(x)
приведет к исключениям и NaN, даже если в идеале математические значения будут работать без проблем.
Все это означает, что вы не можете просто преобразовать точную математическую арифметику в приближенную арифметику с плавающей запятой и ожидать получения хорошего результата (независимо от того, проверяете ли вы равенство или нет). Вы должны учитывать последствия преобразования точной арифметики в приблизительную арифметику и оценивать, как они влияют на вашу программу и ваши данные. Использование арифметики с плавающей запятой, включая сравнения на равенство, должно быть адаптировано к конкретным ситуациям.
abs(a-b)<threshold
. Илиabs(a-b)<threshold*a
. Получение битов с плавающей запятой в F #, вероятно, будет иметь больше накладных расходов, чем выполнение любого из них, поскольку для этого потребуется выделить массив байтов. - person John Palmer   schedule 11.06.2013abs(a-b)<threshold*abs(a)
(с дополнительным прессом()) - это то, что я искал. относительный порог. - person Goswin   schedule 11.06.2013