Проверка равенства с дробями в Common Lisp

Итак, я узнаю, что Lisp делает дроби, и это здорово. Но почему тогда эта проверка на равенство возвращает NIL:

* (= 0.2 1/5)          

NIL

... в то время как он возвращает True, если он сначала преобразуется в float:

* (= 0.2 (float 1/5))

T

Я пробовал в SBCL и CLISP.

Является ли реализация неполной или за этим поведением стоит особая причина или логика?


person Suzana    schedule 01.10.2015    source источник
comment
Неточность чисел с плавающей запятой? См. stackoverflow.com/a/1089026/124319.   -  person coredump    schedule 01.10.2015
comment
также (= 0.5 1/2) возвращает T. 0.5 завершается как в базе 2, так и в 10: stackoverflow.com/a/1096929/124319   -  person coredump    schedule 01.10.2015


Ответы (2)


Соотношения в Common Lisp

Обратите внимание, что дробные числа (которые сами по себе не являются числовыми типами в Common Lisp) преобразуются в рациональные числа в Лиспе. rational, ratio и integer (и другие) являются фактическими числовыми типами в Common Lisp. Если вы вводите дробь, она нормализуется до рационального числа (целого числа или отношения).

CL-USER 16 > 3/9
1/3

CL-USER 17 > 9/9
1

CL-USER 18 > 6/9
2/3

Числовое сравнение

Когда сравниваются число с плавающей запятой и отношение, значение с плавающей запятой преобразуется в рациональное, а затем выполняется точное сравнение. См.: CLHS, Rule of Float and Rational Contagion.

Отношение не преобразуется в число с плавающей запятой, но число с плавающей запятой преобразуется в рациональное.

Проблема возникает из-за того, что некоторые числа с плавающей запятой не преобразуются в ожидаемые соотношения. Основная проблема заключается в том, что числа с плавающей запятой не обязательно имеют точное представление. Преобразование неточного числа в точное рациональное не обязательно дает наивно ожидаемый результат.

К сожалению, преобразование 0.2 в рациональное число не обязательно 1/5, но это:

CL-USER 7 > (rational 0.2)
13421773/67108864

Но 0.5 это 1/2.

CL-USER 8 > (rational 0.5)
1/2

Вот что происходит в ваших примерах:

CL-USER 9 > (= 1/2 (rational 0.5))
T

CL-USER 10 > (= 1/5 (rational 0.2))
NIL

Так что это не

CL-USER 14 > (= 0.2 (float 1/5))
T

Но:

CL-USER 15 > (= (rational 0.2) 1/5)
NIL

Обратите внимание, что тип rational объединяет непересекающиеся подтипы ratio и integer. Таким образом, (rational 1.0) может быть целым числом, а не отношением.

person Rainer Joswig    schedule 01.10.2015

В спецификации о = говорится:

Значение = истинно, если все числа одинаковы по значению; в противном случае это ложь.

В то время как спецификация на real гласит:

Типы rational и float являются непересекающимися подтипами типа real.

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

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

Причина такого поведения в том, что float числа вообще имеют приблизительное представление (см. комментарий @coredump и ссылку), а рациональные числа имеют точное представление, поэтому сравнивать значения, которые могли бы внешне "выглядеть", не очень осмысленно идентичны, но различны во внутреннем (двоичном) представлении.

person Renzo    schedule 01.10.2015