如何解决在 Common Lisp 中测试一个不可知的浮点错误=
为了对涉及大量浮点运算的系统进行一些测试,我定义了浮点运算误差的偏差范围,因此如果两个浮点数之间的差异在偏差范围内,则它们被认为在数学上是相等的:>
;;; Floating Error Agnostic =
;;; F2 is = to F1 within the deviation range +-DEV
(defparameter *flerag-devi* .0005
"Allowed deviation range,within which two floats
should be considered as mathematically equal.")
(defun flerag= (f1 f2 &optional (devi *flerag-devi*))
""
(<= (abs (- f1 f2)) devi))
现在,当我通过比较浮点数和随机分数添加来测试函数时,响应是(至少对于我对函数的测试)是正面的,例如:
(loop repeat 100000000
with f = 1.0
always (flerag= f (+ f (random *flerag-devi*)))) ;T
当我从原始浮点数中减去一个随机分数时也是这种情况,例如:
(loop repeat 100000000
with f = 19.0
always (flerag= f (- f (random *flerag-devi*)))) ;T
(loop repeat 100000000
with f = 3
always (flerag= f (- f (random *flerag-devi*)))) ;T
但是,当我将原始浮点数设置为 1.0(或 1)时:
(loop repeat 100000000
with f = 1.0
always (flerag= f (- f (random *flerag-devi*)))) ;NIL
它总是返回 NIL,这在我看来更奇怪,因为它在评估后立即返回 NIL(在其他情况下,计算 100000000 次需要几秒钟)。到目前为止,这仅发生在 f = 1.0
上,没有其他数字发生,无论对它们的分数进行加法 还是减法。任何人都可以在他/她的 Lisp 上重现这种行为吗?任何解释和帮助将不胜感激。
更新
当我使用双浮点数 1,即 1d0 或通过执行以下操作时,f = 1.0
的情况也适用于其他情况:
(setf *read-default-float-format* 'double-float)
解决方法
欢迎来到浮点运算:一个可怕的陷阱等待着即使是谨慎的人,而粗心的人会立即被仇恨吞噬。请参阅 this,您可以获得 here 的 PDF 副本(此副本可能合法,但像 Oracle 这样的人也有免费提供的版本),而且这篇论文不能免费提供,这有点愚蠢给大家。
这里发生的事情是,假设单个浮点数,在某个时刻 (random 0.0005)
产生一个数字 r
(例如 4.9999706E-4
),它足够接近 0.0005
(- 1.0 r)
是 0.9995
。但是 (- 1.0 0.9995)
比 0.0005
更大:特别是对于单人浮点数(也许对于双人也是如此,但绝对是对于单人),存在单人浮点数 r
使得
(and (< r 0.0005)
(> (- 1.0 (- 1.0 r)) 0.0005)))
可能有可能系统地枚举这样的单个浮点数,但我太懒了。
“足够接近”的定义还取决于您所测量的语义是什么。如果您正在比较物体的直径,您可能不希望出现 x 与 y 都“足够接近”且 x >> y 的情况:1.5E-18
在 {{1} 的 0.0005
之内},但是如果你测量的是质子的半径,你会相差十个数量级,这是一个不小的误差。
像下面这样的定义(这是从前一个有问题的定义修改而来)可能适用于此。
1.5E-8
但是,如果您以摄氏度为单位测量温度,那么您不希望事情在接近零时变得特别挑剔(除非您非常关心冰的形成,在这种情况下您可能会这样做)。
但是我不是浮点专家:我只知道这完全是一场噩梦。
作为一个无关的说明:您的 (defun close-enough-p (f1 f2 &optional (epsilon 0.0005))
(declare (type real f1 f2 epsilon))
(let ((delta (* (abs f1) epsilon)))
(<= (- f1 delta)
f2
(+ f1 delta))))
语法不合法。 loop
需要在 with
或其他迭代构造之前。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。