Common Lisp中带分数的相等性检查

5

我发现Lisp可以处理分数,这很棒。但是为什么下面的等式检查会返回NIL:

* (= 0.2 1/5)          

NIL

如果将它先转换为 float,则返回值为 True:

* (= 0.2 (float 1/5))

T

我已经在 SBCLCLISP 中尝试过。 这种行为是因为实现不完整,还是有特殊的原因或逻辑呢?

1
浮点数的不准确性?请参见https://dev59.com/WXNA5IYBdhLWcg3wHp6B#1089026 - coredump
2
另外,(= 0.5 1/2) 返回 T。0.5 在二进制和十进制中都是有限小数:https://dev59.com/WXNA5IYBdhLWcg3wHp6B#1096929 - coredump
2个回答

6

在Common Lisp中的比例

请注意,分数(本身不是Common Lisp中的数字类型)会在Lisp中转换为有理数rationalratiointeger(以及其他类型)都是Common Lisp中的实际数字类型。如果您输入一个分数,则它将被规范化为有理数(整数或比率数)。

CL-USER 16 > 3/9
1/3

CL-USER 17 > 9/9
1

CL-USER 18 > 6/9
2/3

数字比较

当浮点数和比率进行比较时,浮点数值会被转换为有理数,然后进行精确比较。请参见:CLHS,浮点数和有理数的传染规则

比率不会转换为浮点数,但浮点数会转换为有理数。

问题出现在一些浮点数未被转换为预期的比率上。根本问题在于浮点数并没有必然的精确表示。将非精确数字转换为精确比率,不一定会得到期望的结果。

不幸的是,将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将不相交的子类型ratiointeger组合在一起。因此,(rational 1.0)可能是一个整数,而不是比率。

3

规范中关于=的说明如下:

如果所有数字的值都相同,则=的值为true;否则为false。

而关于real规范则表示:

rationalfloatreal类型的不相交子类型。

换句话说,您可以比较它们,因为它们都是数字,但它们是不同的,因为子类型是不相交的,它们是不同的值。

您可以将它们转换为相同的“子集”数字,即float,然后比较将返回true。

这种行为的原因在于,float数通常具有近似表示(请参见@coredump和链接中的评论),而有理数具有精确表示,因此比较可能“外观”相同但在内部(二进制)表示上不同的值是不太明智的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接