这在常见的 lisp(clisp 和 sbcl)和方案(guile)中都有。虽然这些都是真的:
(= 1/2 0.5)
(= 1/4 0.25)
事实证明这是错误的:
(= 1/5 0.2)
我检查了超规范,它说“=”应该检查数学等价性,尽管参数的类型。到底他妈发生了什么?
这在常见的 lisp(clisp 和 sbcl)和方案(guile)中都有。虽然这些都是真的:
(= 1/2 0.5)
(= 1/4 0.25)
事实证明这是错误的:
(= 1/5 0.2)
我检查了超规范,它说“=”应该检查数学等价性,尽管参数的类型。到底他妈发生了什么?
问题是 0.2 真的不等于 1/5。浮点数不能正确表示 0.2,因此文字 0.2 实际上四舍五入到最接近的可表示浮点数(0.200000001 或类似的东西)。发生此舍入后,计算机无法知道您的数字最初是 0.2 而不是附近的另一个不可表示的数字(例如 0.20000000002)。
至于 1/2 和 1/4 之所以起作用,是因为浮点是基数为 2 的编码,可以准确地表示 2 的幂。
这实际上取决于什么被强制到什么。如果您考虑一下,那么理性更精确,因此强制比较理性而不是浮动是有意义的,但是,如果您有意识地将数字作为浮动进行比较,您可以通过执行以下操作来强制它:
(declaim (inline float=))
(defun float= (a b)
(= (coerce a 'float) (coerce b 'float)))
(float= 0.2 1/5) ; T
实际上......还有更多,因为浮点数为您提供了诸如非数字、正无穷大和负无穷大之类的东西。例如,对于 64 位浮点数,无穷大是 10e200 iirc,因此,没有什么可以阻止您创建一个大于无穷大(或小于负无穷大!)的理性,所以,也许,如果你想要超级精确,你也需要考虑这些情况。同样,与非数字的比较必须始终给您零……
但是,在方案中你有确切的数字,所以你可能会问(注意 #e 前缀,这意味着后面的数字要被准确对待):
> (= 1/5 #e0.2)
#t