Точность с плавающей точкой в ​​F # (и .NET) - PullRequest
2 голосов
/ 03 апреля 2009
  1. В " F # для ученых " Джон Харроп говорит:

    Грубо говоря, значения типа int приближенно действительны числа между min-int и max-int с постоянной абсолютной погрешностью + - 1/2 тогда как значения типа float имеют приблизительно постоянную относительную ошибку, это крошечная доля процента.

    Теперь, что это значит? Тип Int является неточным?

  2. Почему C # for (1 - 0,9) возвращает 0,1, а F # возвращает 0,099999999999978? Является ли C # более точным и пригодным для научных расчетов?

  3. Должны ли мы использовать десятичные значения вместо double / float для научных расчетов?

Ответы [ 4 ]

6 голосов
/ 03 апреля 2009
  1. Для произвольного действительного числа, либо целочисленный тип, либо тип с плавающей запятой дает только приближение. Интегральное приближение никогда не будет отклоняться более чем на 0,5 в одном или другом направлении (при условии, что действительное число находится в пределах диапазона этого интегрального типа). Аппроксимация с плавающей запятой никогда не будет отключена более чем на небольшой процент (опять же, при условии, что действительное находится в диапазоне значений, поддерживаемых этим типом с плавающей запятой). Это означает, что для меньших значений типы с плавающей запятой будут обеспечивать более близкие приближения (например, сохранение приближения для PI в плавающей запятой будет намного более точным, чем int-приближение 3). Однако для очень больших значений приближенное значение целочисленного типа будет на самом деле лучше, чем у типа с плавающей запятой (например, рассмотрим значение 9223372036854775806.7, которое только на 0,3, если представлено как 9223372036854775807 как длинное, но которое представлено 9223372036854780000.00000000 при хранении как поплавок).

  2. Это просто артефакт того, как вы печатаете значения. 9/10 и 1/10 не могут быть точно представлены как значения с плавающей запятой (потому что знаменатель не является степенью двойки), так же как 1/3 не может быть точно записана в виде десятичной дроби (вы получите 0,333 ... где 3-е повторяются вечно). Независимо от того, какой язык .NET вы используете, внутреннее представление этого значения будет одинаковым, но разные способы печати значения могут отображать его по-разному. Обратите внимание, что если вы оцениваете 1,0 - 0,9 в FSI, результат отображается как 0,1 (по крайней мере, на моем компьютере).

  3. Какой тип вы будете использовать в научных вычислениях, будет зависеть от того, чего именно вы пытаетесь достичь. Ваш ответ, как правило, будет только приблизительно точным. Насколько точным оно должно быть? Каковы ваши требования к производительности? Я считаю, что десятичный тип на самом деле является числом с фиксированной запятой, что может сделать его неподходящим для вычислений с очень маленькими или очень большими значениями. Также обратите внимание, что F # включает рациональные числа произвольной точности (с типом BigNum), которые также могут быть подходящими в зависимости от вашего ввода.

2 голосов
/ 03 апреля 2009

Для первого пункта я бы сказал, что он говорит, что int может использоваться для представления любого действительного числа в диапазоне целых чисел с постоянной максимальной ошибкой в ​​[-0,5, 0,5]. Это имеет смысл. Например, pi может быть представлено целочисленным значением 3 с ошибкой, меньшей 0,15.

Числа с плавающей запятой не разделяют это свойство; их максимальная абсолютная ошибка не зависит от значения, которое вы пытаетесь представить.

2 голосов
/ 03 апреля 2009

Нет, F # и C # используют один и тот же двойной тип. Плавающая точка почти всегда неточна. Целые числа точны, хотя.

UPDATE:

Причина, по которой вы видите разницу, заключается в печати номера, а не фактического представления.

0 голосов
/ 03 апреля 2009

3 - это зависит от расчетов: иногда float - хороший выбор, иногда вы можете использовать int. Но есть задачи, когда вам не хватает точности для любого из чисел с плавающей запятой и десятичных.

Причина против использования int :

> 1/2;; 
val it : int = 0

Причина против использования float (также известный как double в C #):

> (1E-10 + 1E+10) - 1E+10;;
val it : float = 0.0

Причина против BCL десятичная :

> decimal 1E-100;;
val it : decimal = 0M

У каждого перечисленного типа есть свои недостатки.

...