All~ A good "close enough" comparison is actually very difficult. I adapted one based on
http://adtmag.com/Articles/2000/03/16/Comparing-Floats-How-To-Determine-if-Floating-Quantities-Are-Close-Enough-Once-a-Tolerance-Has-Been.aspx?Page=1 which provides a pretty good example and handles all the cases that I could find. public static boolean compareDoubles(double tolerance, double d1, double d2) { // For a discussion and analysis of this technique, check out: // http://adtmag.com/Articles/2000/03/16/Comparing-Floats-How-To-Determine-if-Floating-Quantities-Are-Close-Enough-Once-a-Tolerance-Has-Been.aspx?Page=1 // handle 0.0 specifically, as it will screw with our division if (d1 == 0.0 || d2 == 0.0) { return Math.abs(d1 - d2) < tolerance; } // catch overflow cases, we will divide by d1 which could increase the value of d2 // but use absolute value to handle all the different permutations of signs double d1abs = Math.abs(d1); double d2abs = Math.abs(d2); if ((d1abs < 1.0) && (d2abs > d1abs*Double.MAX_VALUE)) { return false; } // comparing that they have a ratio near 1.0 handles both very large // and very small numbers cleanly with a single tolerance double ratio = d2 / d1; return ((1-tolerance) < ratio && ratio < (1+tolerance)); } I use a default tolerance of 1e-6, which works fairly well. Enjoy, Matt On Sat, Oct 16, 2010 at 7:05 PM, Steven E. Harris <s...@panix.com> wrote: > cej38 <junkerme...@gmail.com> writes: > > > (defn float= > > ([x y] (float= x y 0.00001)) > > ([x y epsilon] > > (let [scale (if (or (zero? x) (zero? y)) 1 (Math/abs x))] > > (<= (Math/abs (- x y)) (* scale epsilon)))) ) > > You're scaling epsilon incorrectly here. Epsilon defines the smallest > value that yields a value greater than one when added to one. If you're > not using it along with one, you're using it incorrectly. > > What you need to do is scale epsilon by having its exponent match the > value with which you want to use it, while maintaining its mantissa. The > C library offers functions ldexp()¹ and frexp()² to split and scale the > exponent of a floating point number, respectively; Common Lisp offers > DECODE-FLOAT³ and SCALE-FLOAT for the same purpose. > > I don't know of any standard functions in Java -- or Clojure -- that > allow one to destructure a floating point number like this. It's > possible to use Float#floatToRawIntBits(), an understanding of IEEE 754, > and tweezers to get there. > > If you assume you have a function called `scaled-epsilon' that accepts > the exemplar value with which you intend to use epsilon, > > (defn scaled-epsilon [n] ...) > > you can use the following function `same?' to compare your floating > point values, assuming they're nonnegative: > > ,---- > | (defn same? > | [m n] > | (if (< n m) > | (sufficiently-close? n m) > | (sufficiently-close? m n))) > | > | (defn- sufficiently-close? > | [smaller larger] > | (or (zero? larger) > | (if (zero? smaller) > | (< larger (scaled-epsilon 0.0))) > | (zero? (- 1 (/ smaller larger))))) > `---- > > Note too that scaling epsilon must take into account whether you intend > to add it or subtract it from some companion number. It's common to use > the word "epsilon" to mean some fudge factor without considering what > the value really means for a floating point number. Indeed, using it > properly in Java is still too difficult. > > > Footnotes: > ¹ http://www.dinkumware.com/manuals/?manual=compleat&page=math.html#frexp > ² http://www.dinkumware.com/manuals/?manual=compleat&page=math.html#ldexp > ³ > http://www.lispworks.com/documentation/HyperSpec/Body/f_dec_fl.htm#decode-float > > http://www.lispworks.com/documentation/HyperSpec/Body/f_dec_fl.htm#scale-float > > -- > Steven E. Harris > > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com<clojure%2bunsubscr...@googlegroups.com> > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en