Clojure: непоследовательное округление в вычитаниях

Я работаю над частью кода, где числовое равенство является важным фактором в нескольких логических условных. Clojure делает то, что я не знаю достаточно, чтобы объяснить. Например:

user=> (- 5 4.9)
0.09999999999999964
user=> (- 5 4.8)
0.20000000000000018
user=> (- 5 2.9)
2.1
user=> (- 5 2.7)
2.3
user=> (- 5 2.8)
2.2
user=> (- 9 6.9)
2.0999999999999996 
user=> (- 9 2.9)
6.1

Кажется, что в некоторых обстоятельствах числовые значения Clojure понимают вычитание до 0,1, в других - нет. Что здесь происходит?

2 ответа

Решение

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

user=> (- 5M 4.9M)
0.1M
user=> (- 5M 4.8M)
0.2M
user=> (- 9M 6.9M)
2.1M
user=> (- 9M 2.9M)
6.1M

Чтобы сделать то же самое с десятичной переменной, используйте форму bigdec:

user=> (def k 4.9)
#'user/k
user=> (- 5M k)
0.09999999999999964
user=> (- 5M (bigdec k))
0.1M

Добавление некоторых Ms к числу литералов, о которых идет речь, касается симптомов, но ничего не делает для улучшения понимания Дэвидом основ математики с плавающей запятой. Посмотрите классическое " Что должен знать каждый компьютерный ученый" об арифметике с плавающей запятой, или любой из многочисленных вопросов по SO о неточности с плавающей запятой. Вот два примера, которые я мог легко найти; Я видел по крайней мере три только для Clojure, но их трудно найти с помощью поиска.

Другие вопросы по тегам