emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: Elisp manual, node "Comparison of Numbers"


From: Drew Adams
Subject: RE: Elisp manual, node "Comparison of Numbers"
Date: Mon, 29 May 2006 11:14:50 -0700

    >   (defun approx-equal (x y &optional fuzz)
    >     (setq fuzz (or fuzz 1.0e-8))
    >     (cond ((= x 0.0) (< y fuzz))
    >           ((= y 0.0) (< x fuzz))
    >           (t (< (/ (abs (- x y)) (max (abs x) (abs y))) fuzz))))

    Leonard> Looks reasonable, but you have to use (< (abs y) fuzz) etc.

Right - forgot the abs. Thx.

    David> The problem here is that fuzz is a _relative_ measure of
    equality, and you employ it as an absolute measure here.

FUZZ is a relative measure in the final clause and an absolute measure in
the first two clauses. Anyway, you are right that my definition mixes
absolute with relative measures. It would also be right to say that the same
fuzz factor might not always be useful as both an absolute measure and a
relative measure this way.

My point was that the relative measure given is useless as a measure of
proximity to zero. Or, as you echoed it, "Relative measures don't work when
comparing with 0." The difference from zero compared to the maximum is
always exactly 1.0. By that relative measure, neither 1,000,000,000 nor
0.0000000001 is approximately zero - they fail equality to the same degree
(Animal Farm, revisited), as do all other floating-point numbers (except
0.0, which causes division by zero unless treated as a special case).

Neither absolute nor relative measure is appropriate in all cases - there is
no general comparison algorithm that is good all of the time (duh). Some
applications use absolute comparison (which has the drawback of being
relatively different comparing small numbers from comparing large numbers);
others use relative comparison (sacrificing approximate equality with zero);
still others use some combination of the two. And of course approximate
equality (by most definitions) is not transitive, whether measured
absolutely or relatively.

One article I came across put it this way: "Depending on how you came up
with the numbers, you may have 1.0 not being approximately equal to
0.999999, or you may have 1.0 being approximately equal to 0.0."

Here's a definition I came across that combines absolute and relative
comparisons in a way that is better than what I proposed. (I added the
defaults for the fuzz factors.)

(defun approx-equal (x y &optional rfuzz afuzz)
  "Return non-nil if numbers X and Y are approximately equal.
RFUZZ is a relative fuzz factor.  AFUZZ is an absolute fuzz factor.
RFUZZ defaults to 1.0e-8.  AFUZZ defaults to (/ RFUZZ 10).
The algorithm is:
 (< (abs (- X Y)) (+ AFUZZ (* RFUZZ (+ (abs X) (abs Y)))))."

  (setq rfuzz (or rfuzz 1.0e-8) afuzz (or afuzz (/ rfuzz 10)))
  (< (abs (- x y)) (+ afuzz (* rfuzz (+ (abs x) (abs y))))))

This is more flexible than both the definition I gave and the one in the
manual. You can of course still shoot yourself in the foot with this,
depending on the values you are testing and your choice of fuzz factors. In
my application this definition works well, as does the code I sent
originally: an absolute test against zero is in fact what I need.

Wrt the Elisp manual, I'd suggest 1) going with the original example, 2)
pointing out that it does not work for testing approximation to zero, and 3)
mentioning that an absolute test can sometimes be appropriate in that case.

FYI - These articles are interesting on testing floating-point approximate
equality: http://docs.sun.com/source/806-3568/ncg_goldberg.html,
http://www.visoracle.com/squeakfaq/float-precision.html. Knuth's Art of
Computer Programming vol 2 has a section on this which is also interesting.






reply via email to

[Prev in Thread] Current Thread [Next in Thread]