Common Lisp 中分数的相等性检查

Equality check with fractions in Common Lisp

所以我了解到 Lisp 可以处理分数,这很棒。但是为什么这个相等性检查 return NIL:

* (= 0.2 1/5)          

NIL

...如果先转换为 float,则 return 为真:

* (= 0.2 (float 1/5))

T

我在 SBCLCLISP 中尝试过。

实施是否不完整,或者此行为背后是否有特殊原因或逻辑?

关于=specification说:

The value of = is true if all numbers are the same in value; otherwise it is false.

虽然 real 上的 specification 说:

The types rational and float are disjoint subtypes of type real.

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

你可以做的是将它们转换为相同的数字“子集”,即浮点数,然后比较结果将 return 为真。

此行为的原因是 float 数字通常具有近似表示(请参阅@coredump 和 link 的评论),而有理数具有精确表示,因此它比较外部“看起来”相同但内部(二进制)表示不同的值不是很明智。

Common Lisp 中的比率

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

CL-USER 16 > 3/9
1/3

CL-USER 17 > 9/9
1

CL-USER 18 > 6/9
2/3

数值比较

比较float和ratio时,先将float值转为有理数,再进行精确比较。参见:CLHS, Rule of Float and Rational Contagion.

比例没有转换成浮点数,而是将浮点数转换成有理数

问题的出现是因为某些浮点数未转换为您期望的比率。潜在的问题是浮点数不一定具有 exact 表示。不需要将非精确数转换为精确有理数,从而给出天真的预期结果。

不幸的是0.2转换为有理数不一定是1/5,但是这个:

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

但是 0.51/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) 可能是整数,而不是比率。