unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: "Drew Adams" <drew.adams@oracle.com>
Subject: RE: Elisp manual, node "Comparison of Numbers"
Date: Mon, 29 May 2006 11:14:50 -0700	[thread overview]
Message-ID: <MEEKKIABFKKDFJMPIOEBIENEDBAA.drew.adams@oracle.com> (raw)
In-Reply-To: <447AF9B8.2080801@student.lu.se>

    >   (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	other threads:[~2006-05-29 18:14 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-05-29 13:20 Elisp manual, node "Comparison of Numbers" Drew Adams
2006-05-29 13:33 ` David Kastrup
2006-05-29 13:42   ` Kim F. Storm
2006-05-29 13:54     ` Lennart Borgman
2006-05-29 14:08       ` David Kastrup
2006-05-29 14:18         ` Lennart Borgman
2006-05-29 14:23           ` David Kastrup
2006-05-29 14:40             ` Lennart Borgman
2006-05-29 21:28   ` Richard Stallman
2006-05-29 13:40 ` Lennart Borgman
2006-05-29 18:14   ` Drew Adams [this message]
2006-05-30  3:46     ` Richard Stallman
2006-05-29 19:23 ` Stefan Monnier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=MEEKKIABFKKDFJMPIOEBIENEDBAA.drew.adams@oracle.com \
    --to=drew.adams@oracle.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).