首页 > 解决方案 > 在 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 上重现这种行为吗?任何解释和帮助将不胜感激。

更新f = 1.0当我使用双浮点 1(即 1d0 或通过执行以下操作)时, 案例与其他案例也一样:

(setf *read-default-float-format* 'double-float)

标签: common-lisp

解决方案


欢迎来到浮点运算:一个可怕的陷阱等待着即使是谨慎和粗心的人也会立即被咕噜声吃掉。看到这个你可以在这里得到一个 PDF 副本(这个副本可能是合法的,但是像 Oracle 这样的人也有免费的版本),而且这篇论文并不是免费提供给所有人的,这有点愚蠢。

这里发生的是,假设单个浮点数,在某些时候(random 0.0005)会产生一个足够接近的数字r(例如)。但是大于:特别是对于单花车(也许也适用于双打,但绝对是单打)存在单花车,使得4.9999706E-40.0005(- 1.0 r)0.9995(- 1.0 0.9995)0.0005r

(and (< r 0.0005)
     (> (- 1.0 (- 1.0 r)) 0.0005)))

可能有可能系统地枚举这样的单个浮点数,但我太懒了。

“足够接近”意味着什么的定义还取决于您所测量的语义是什么。如果您正在比较物体的直径,您可能不希望出现 x 与 y '足够接近'且 x >> y:1.5E-18在 范围内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需要在之前for或其他迭代构造。


推荐阅读