Вопрос

Недавно я просматривал учебник по Haskell и заметил такое поведение, когда пробовал использовать некоторые простые выражения Haskell в интерактивном режиме. ghci оболочка:

Prelude> 1.1 + 1.1 == 2.2
True
Prelude> 1.1 + 1.1 + 1.1 == 3.3
False
Prelude> 1.1 + 1.1 + 1.1 > 3.3
True
Prelude> 1.1 + 1.1 + 1.1
3.3000000000000003

Кто-нибудь знает, почему это так?

Это было полезно?

Решение

Потому что 1.1 а также 3.3 находятся Номера плавучих точек. Анкет Десятичные фракции, такие как .1 или .3, не совсем репрезентативны в бинарном номере плавающей запятой. .1 означает 1/10. Чтобы представить это в бинарном, где каждая фракционная цифра представляет 1/2не (1/2, 1/4, 1/8 и т. Д.), Вам понадобится бесконечное количество цифр, 0,000110011 ... Повторяя бесконечно.

Это точно та же проблема, что и представление, скажем, 1/3 в базе 10. В базе 10 вам потребуется бесконечное количество цифр, .33333 ... навсегда, чтобы представлять 1/3 точно. Итак, работая в базе 10, вы обычно окружаете, к чему -то .33. Но если вы добавите три копии этого, вы получите 0,99, а не 1.

Для гораздо больше информации по этой теме прочитайте Что каждый компьютерный ученый должен знать о арифметике с плавающей запятой.

Для более точного представления рациональных чисел в Haskell вы всегда можете использовать рациональный тип данных, Ratio; в сочетании с биньями (произвольно большими целыми числами, Integer в Хаскелле, в отличие от Int которые являются фиксированным размером) в качестве типа для числителя и знаменателя, вы можете представлять произвольно точные рациональные числа, но с значительно более медленной скоростью, чем числа плавающей запятой, которые реализованы в оборудовании и оптимизированы для скорости.

Номера с плавающими темпами являются оптимизация для научных и численных вычислений, которые обменяют точность для высокой скорости, что позволяет вам выполнять очень большое количество вычислений в небольшом времени, если вы знаете об округлении и о том, как это влияет на ваши вычисления Анкет

Другие советы

Потому что числа с плавающей запятой не являются точными(Википедия)

Вы можете избежать ошибок с плавающей запятой в Haskell, используя рациональные типы:

Prelude Data.Ratio> let a = (11 % 10) + (11 % 10) + (11 % 10)
Prelude Data.Ratio> a > (33 % 10)
False
Prelude Data.Ratio> fromRational a
3.3

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

Похоже на типичную проблему ошибки с плавающей запятой.

Видеть Что является простым примером ошибки с плавающей запятой/округление?

Это связано с тем, как работают числа плавающей запятой IEEE.

1.1 представлен как 1.100000000000000001 в плавающей запятой, 3,3 представлен как 3,299999999999998.

Итак, 1.1 + 1,1 + 1.1 на самом деле

1.1000000000000001 + 1.1000000000000001 + 1.1000000000000001 = 3.3000000000000003

Что, как вы можете видеть, на самом деле больше 3,299999999999998.

Обычный обходной путь состоит в том, чтобы не оценивать равенство, либо проверить, находится ли число в целевой +/- небольшой эпсилоне (что определяет точность, которая вам нужна).

Пример: если оба истины, то сумма «равна» 3,3 (в пределах допустимой ошибки).

1.1 + 1.1 + 1.1 < 3.3 + 1e9 
1.1 + 1.1 + 1.1 > 3.3 - 1e9

Немного поплавка может быть выражено точно с использованием представления IEEE 754, поэтому они всегда будут немного не снимаются.

В целом, вы не должны сравнивать поплавки по равенству (по причинам, изложенным выше). Единственная причина, по которой я могу придумать, это если вы хотите сказать: «Изменилась ли это значение?» Например, «if (newscore /= oldScore)», затем примите какое -то действие. Это нормально, пока вы не сравниваете результат двух отдельных вычислений, чтобы проверить, являются ли они равными (потому что тогда даже математически, если они есть, они могли бы окружить иначе).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top