Lisp (=1/50.2)是否为假?

Lisp (=1/50.2)是否为假?,lisp,scheme,common-lisp,Lisp,Scheme,Common Lisp,这在公共lisp(clisp和sbcl)和scheme(guile)中都是如此。虽然这些都是事实: (= 1/2 0.5) (= 1/4 0.25) 事实证明这是错误的: (= 1/5 0.2) 我检查了hyperspec,它说“=”应该检查数学等价性,不管参数的类型如何。到底是怎么回事?问题是0.2实际上并不等于1/5。浮点数不能正确地表示0.2,因此文字0.2实际上被四舍五入到最接近的可表示浮点数(0.200000001或类似的数字)。在此舍入发生后,计算机无法知道您的数字原来是0.2,

这在公共lisp(clisp和sbcl)和scheme(guile)中都是如此。虽然这些都是事实:

(= 1/2 0.5)
(= 1/4 0.25)
事实证明这是错误的:

(= 1/5 0.2)

我检查了hyperspec,它说“=”应该检查数学等价性,不管参数的类型如何。到底是怎么回事?

问题是0.2实际上并不等于1/5。浮点数不能正确地表示0.2,因此文字0.2实际上被四舍五入到最接近的可表示浮点数(0.200000001或类似的数字)。在此舍入发生后,计算机无法知道您的数字原来是0.2,而不是附近的另一个不可表示的数字(例如0.200000000002)


至于1/2和1/4工作的原因,是因为浮点是一种基2编码,可以准确地表示二的幂。

这实际上取决于强制什么。如果你想一想,那么rational更精确,因此强迫rational进行比较是有意义的,而不是浮点数。然而,如果你有意识地想将数字作为浮点数进行比较,你可以通过如下操作来强迫它:

(declaim (inline float=))
(defun float= (a b)
  (= (coerce a 'float) (coerce b 'float)))
(float= 0.2 1/5) ; T

实际上。。。还有更多,因为浮动为您提供了非数字、正无穷大和负无穷大。例如,对于64位浮点来说,无穷大是10e200 Irc,所以,没有什么能阻止你创建一个有理的大无穷大(或者小于负无穷大),所以,也许,如果你想要超精确的话,你也需要考虑那些情况。同样,与非数字的比较必须始终为零…

请阅读

但是,在方案中,您有确切的数字,因此您可以询问(注意#e前缀,这意味着下面的数字将被精确处理):


二进制表示可能不相等,在这种情况下,您需要检查它们是否近似相等。这在大多数计算机语言中都很常见。除非您使用的数据类型具有正确的精度,否则计算的实际结果可能不是您所想的。@Darthtater是的,但鉴于“=”的规范,二进制表示形式或数据类型应该不重要,因为“=”指定用于检查数学等效性,尽管二进制表示/数据类型不同。实现者的工作是检查“=”是否按照规范所说的那样工作,不是吗?虽然我明白,如果浮点格式不提供检查精度,可能会很困难。这些实现应该找到一个解决办法,不是吗?您可能想建议的是彻底禁止flonums,并且只使用十进制浮点数。这会起作用,但速度要慢得多,因为没有通用处理器本机支持它们,所以所有这些都必须在软件中实现。但这不符合“=”的规范吗?为什么不做一个变通办法呢?与此类似,当遇到“=”时,使用BCD或right在参数的ascii表示形式中计算参数中的所有浮点计算。或者更好,如果遇到浮点参数,请使用浮点计算所有其他参数。没关系,我认为第二个选项不起作用。我知道第一个有什么问题。我只是感到困惑,虽然标准说了一件事,但实现却做了一些不同的事情。我一直认为Lisp努力追求数学的正确性,因为他们使用有理数而不是浮点来表示分数。这有点令人沮丧。@JanHlavacek你没试过,是吗?它不起作用,还是假的。如果真是这样,那就太好了。哦,天哪,估计是13421773/67108864。我不敢相信这对lisp是正确的。我的观点是,它的计算结果是
13421773/67108864
。所以它不等于1/5。这并不奇怪,因为
0.2
实际上代表
0.2
的浮点表示,而不是0.2。有趣的是,在Maxima中:
:lisp(=1/50.2)
的计算结果为
T
,但
:lisp(rational 0.2)
3602879701896397/18014398098509481984
,这取决于实现,顺便说一句,一些实现实现将
#e0.2
作为
(不精确->精确0.2)
的读取时间。
> (= 1/5 #e0.2)
#t