;;; fastfuzz.el --- Compensate fast for floating-point roundoff error ;; Copyright (C) 1998 Will Mengarini ;; Author: Will Mengarini ;; URL: ;; Created: Su 15 Mar 98 ;; Version: 0.20, Mo 04 May 98 ;; Keywords: extensions, float, floating point, fuzz ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; Try this with C-x C-e: ;; (= (+ 0.1 0.2) 0.3) ;; Running on an Intel 486/50 with hardware floating point, GNU 19.34.1 ;; returns `nil' because of a general problem with the imprecise ;; representation of floating point numbers. This package implements ;; functions that compensate for that. For a general discussion of the ;; issue, see the Info node elisp|Numbers|Comparison of Numbers. ;; To use this package, first you'll need to copy this file to a directory ;; that appears in your load-path. `load-path' is the name of a variable ;; that contains a list of directories Emacs searches for files to load. ;; To prepend another directory to load-path, put a line like ;; (add-to-list 'load-path "c:/My_Directory") in your .emacs file. ;; Then, put ;; (require 'fastfuzz) ;; in your .emacs file. That will make these functions available: ;; fastfuzzy= (x y) ;; fastfuzzy= (x y) ;; fastfuzzy> (x y) ;; fastfuzzyzerop (x) ;; fastfuzzyplusp (x) ;; fastfuzzyminusp (x) ;; fastfuzzyintegerp (x) ;; fastfuzzywholenump (x) ;; Each has a meaning analogous to the corresponding bare function, except ;; that numbers are considered equal if they're "close enough", which is ;; defined in terms of this package's global variable `fastfuzz'. ;; The difference between this package and fuzz.el is that this package ;; defines equality without using division, so it's faster, but doesn't work ;; at extreme orders of magnitude. For example, on an Intel 486, ;; (let ((fuzz 1e-14) ;; (fastfuzz 1e-14)) ;; (and (fastfuzzy= 1e-13 1.1e-13) ;; (not (fuzzy= 1e-13 1.1e-13)) ;; (fuzzy= 1e11 (/ (* 1e11 3.3) (+ 1.1 2.2))) ;; (not (fastfuzzy= 1e11 (/ (* 1e11 3.3) (+ 1.1 2.2)))))) ;; An example of an appropriate application for fastfuzz.el would be checking ;; whether probabilities sum to 1.0. ;; You might need a different `fastfuzz' for your machine. To find out, try ;; M-x fastfuzz-selftest. If you get an error, try increasing `fastfuzz' ;; with M-x set-variable. When you find a value that works, put a line like ;; (setq fastfuzz 1.0e-4) ;; in your .emacs file. ;; If you need a different fastfuzz for a particular application, you could ;; localize the variable `fastfuzz' in a `let' form, or make it buffer-local. ;; Note that this package doesn't share fuzz.el's property that ;; every dyadic comparison function is defined in terms of ;; `fastfuzzy=' (mutatis mutandi), so you can't just overwrite that ;; one function to modify the package's definition of equality. ;;; Code: ;;;###autoload (defvar fastfuzz 1.0e-6 "*Two floats with magnitudes closer than this are considered fastfuzzy=.") ;;;###autoload (defsubst fastfuzzyzerop (x) "Return t if NUMBER is within `fastfuzz' of zero." ( x fastfuzz)) ;;;###autoload (defsubst fastfuzzyminusp (x) "Return t if NUMBER is negative and not within `fastfuzz' of zero." ( (abs (- x y)) fastfuzz)) ;;;###autoload (defsubst fastfuzzy> (x y) "Return t if first arg is greater than second, and they're not nearly equal." (> (- x y) fastfuzz)) ;;;###autoload (defsubst fastfuzzy (- y x) fastfuzz)) ;;;###autoload (defsubst fastfuzzy>= (x y) "Return t if first arg is greater than second, or they're nearly equal." (= x 0.0))) ;;; Selftest: ;; ;; Use `delete-rectangle', orthodoxily bound to C-x r d, to uncomment this ;; code for automated regression testing. It's commented out only to save ;; space (at RMS's request) in released Emacs; if you're going to hack it you ;; probably want to leave the selftest enabled. ;; ;; (require 'cl) ;; ;; (defun fastfuzz-selftest () ;; "Test the functions in fastfuzz.el: `fastfuzzy=', etc. ;; Signal an error if a test fails. ;; This selftest is not automatically run when fastfuzz.el is loaded, ;; so if you're on a new machine, or building a new Emacs, ;; you might want to run it by hand with \\[fastfuzz-selftest]." ;; ;; See discussion of naming convention in fuzz.el's fuzz-selftest. ;; (interactive) ;; ;; ;; This test suite is nothing like a comprehensive one; it just ;; ;; tries to catch the most egregious brain farts. ;; ;; Contributions would be welcomed. ;; ;; With C-x C-e, this code ;; ;; (mapcar (lambda (x) (format "%.18g" x)) [.1 .2 .3]) ;; ;; (mapcar (lambda (x) (format "%.18g" x)) [.4 .5 .6]) ;; ;; (mapcar (lambda (x) (format "%.18g" x)) [.7 .8 .9]) ;; ;; may be useful in getting an idea how to construct problem expressions. ;; ;; (assert (fastfuzzyplusp 0.01)) ;; (assert (fastfuzzyminusp -0.01)) ;; ;; (assert (not (fastfuzzyzerop 0.01))) ;; (assert (not (fastfuzzyzerop -0.01))) ;; ;; (assert (fastfuzzy= (* (/ 10.0 3.0) 3.0) 10.0)) ;; (assert (fastfuzzy= (* (/ 01.0 3.0) 3.0) 01.0)) ;; ;; (assert (fastfuzzy= (+ 0.1 0.2) 0.3)) ;; (assert (fastfuzzy= (+ 0.1 0.2) 0.3)) ;; (assert (not (fastfuzzy/= (+ 0.1 0.2) 0.3))) ;; (assert (not (fastfuzzy> (+ 0.1 0.2) 0.3))) ;; (assert (not (fastfuzzy 0.0 0.0))) ;; (assert (not (fastfuzzy> 0.0 0.1))) ;; ;; (assert (fastfuzzy>= 1.0 1.0)) ;; (assert (fastfuzzy>= 1.1 1.0)) ;; ;; (assert (not (fastfuzzy +2.0 +3.0))) ;; (assert (not (fastfuzzy>= +2.0 +3.0))) ;; ;; (assert (not (fastfuzzy +2.0 -3.0)) ;; (assert (fastfuzzy>= +2.0 -3.0)) ;; ;; (assert (fastfuzzy -2.0 +3.0))) ;; (assert (not (fastfuzzy>= -2.0 +3.0))) ;; ;; (assert (not (fastfuzzy -2.0 -3.0)) ;; (assert (fastfuzzy>= -2.0 -3.0)) ;; ;; (assert (fastfuzzyzerop fastfuzz)) ;; (assert (fastfuzzyintegerp fastfuzz)) ;; (assert (fastfuzzywholenump fastfuzz)) ;; ;; (assert (fastfuzzyzerop 0.0)) ;; (assert (fastfuzzyintegerp 0.0)) ;; (assert (fastfuzzywholenump 0.0)) ;; ;; (assert (fastfuzzyintegerp 1.0)) ;; (assert (fastfuzzywholenump 1.0)) ;; ;; (assert (fastfuzzyintegerp -1.0)) ;; ;; (message "Fastfuzz selftest successful") ;; ;; ) ;for C-x C-e: (fastfuzz-selftest) ;; ;;; End of selftest (provide 'fastfuzz) ;;; fastfuzz.el ends here