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
我在 SBCL
和 CLISP
中尝试过。
实施是否不完整,或者此行为背后是否有特殊原因或逻辑?
关于=
的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 中被转换为 rationals。 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
数值比较
比较float和ratio时,先将float值转为有理数,再进行精确比较。参见:CLHS, Rule of Float and Rational Contagion.
比例没有转换成浮点数,而是将浮点数转换成有理数
问题的出现是因为某些浮点数未转换为您期望的比率。潜在的问题是浮点数不一定具有 exact 表示。不需要将非精确数转换为精确有理数,从而给出天真的预期结果。
不幸的是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)
可能是整数,而不是比率。
所以我了解到 Lisp 可以处理分数,这很棒。但是为什么这个相等性检查 return NIL:
* (= 0.2 1/5)
NIL
...如果先转换为 float
,则 return 为真:
* (= 0.2 (float 1/5))
T
我在 SBCL
和 CLISP
中尝试过。
实施是否不完整,或者此行为背后是否有特殊原因或逻辑?
关于=
的specification说:
The value of
=
is true if all numbers are the same in value; otherwise it is false.
虽然 real
上的 specification 说:
The types
rational
andfloat
are disjoint subtypes of typereal
.
换句话说,你可以比较它们,因为它们都是数字,但它们不同,因为子类型不相交,它们不同 值。
你可以做的是将它们转换为相同的数字“子集”,即浮点数,然后比较结果将 return 为真。
此行为的原因是 float
数字通常具有近似表示(请参阅@coredump 和 link 的评论),而有理数具有精确表示,因此它比较外部“看起来”相同但内部(二进制)表示不同的值不是很明智。
Common Lisp 中的比率
请注意 fractions(在 Common Lisp 中它本身不是数字类型)在 Lisp 中被转换为 rationals。 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
数值比较
比较float和ratio时,先将float值转为有理数,再进行精确比较。参见:CLHS, Rule of Float and Rational Contagion.
比例没有转换成浮点数,而是将浮点数转换成有理数
问题的出现是因为某些浮点数未转换为您期望的比率。潜在的问题是浮点数不一定具有 exact 表示。不需要将非精确数转换为精确有理数,从而给出天真的预期结果。
不幸的是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)
可能是整数,而不是比率。