From: William James Date: 2007-09-13T16:15:12+09:00 Subject: Re: wierd floating point output On Sep 12, 5:13 am, Michael Ulm wrote: > William James wrote: > > On Sep 3, 9:02 pm, Stefan Rusterholz wrote: > >> William James wrote: > >>> On Sep 3, 5:32 pm, Stefan Rusterholz wrote: > >>>>>> using delta comparison anyway too. > >>>> end > >>>> (0.01 + 0.05).in_delta?(0.06) # => true > >>>> 0.01 + 0.05 =~ 0.06 # => true # can't set the delta, is similarly evil > >>>> as == > >>> class Numeric > >>> def in_delta?(other, delta=Float::EPSILON*16) # chose the delta > >>> wisely > >>> (self-other).abs < delta > >>> end > >>> alias =~ in_delta? > >>> end > >>> p 1.23456789e4.in_delta?( 1.234567892e4 ) > >>> p 1.23456789e-1.in_delta?( 1.234567892e-1 ) > >>> p 1.23456789e-6.in_delta?( 1.234567892e-6 ) > >>> p 1.23456789e-11.in_delta?( 1.234567892e-11 ) > >>> ==== output ==== > >>> false > >>> false > >>> true > >>> true > >> That's why, chose your delta wisely. It is also why the =~ is only > >> marginally less surprising than == and still evil. > >> Maybe one could improve it a bit using a delta created using the > >> magnitude (hello, dear log). Another approach might be to use the > >> quotient of the two numbers (its proximity to 1). > > > Yes, having manually to choose the delta for each > > comparison is not good. > > > Here is a modificaion of a method mentioned > > by Michael Ulm earlier. > > > def eq( x, y ) > > x == y or > > (x-y).abs < (x.abs + y.abs) * Float::EPSILON * 4 > > end > > > DATA.each{|s| > > strings = s.chomp.split(";") > > floats = strings.map{|s| eval(s) } > > puts strings.join( " (x-y).abs < (x.abs + y.abs) *== " ) + > > " : #{ floats[0]==floats[1] } #{ eq( *floats ) }" > > } > > --snip-- > > Please note, that the formula I gave has a bug in it: > The condition > > (x-y).abs < (x.abs + y.abs) * Delta > > is always false if y == 0 (and Delta < 1 :-) > Better use something along the lines of > > (x-y).abs < (x.abs + y.abs + 1.0) * Delta This works pretty well, but seems to have a problem with very small numbers. In the program below, this method (eq1) incorrectly reports that these are equal: 1.23456789e-11 == 1.234567892e-11 1.23456789e-16 == 1.234567892e-16 The function eq2 is more complex, but it seems more consistent. def eq1( x, y, epsilon = Float::EPSILON * 8 ) x == y or (x-y).abs < (x.abs + y.abs + 1.0) * epsilon end def eq2( x, y, epsilon = Float::EPSILON * 8 ) return true if x == y return true if (i = [x,y].index( 0.0 )) && ( [x,y][1-i].abs < epsilon ) return (x-y).abs < (x.abs + y.abs) * epsilon end DATA.each{|s| strings = s.chomp.split(/ *; */) equality = ( strings.pop == "=" ).to_s floats = strings.map{|s| eval(s) } results = [ :eq1, :eq2 ].map{|fun| send( fun, *floats ).to_s }.map{|s| if s == equality; s else s.upcase end } puts strings.map{|s| s + if p = s.index('e'); " " * (4 - (s.size - p)) else "" end }. join( " == " ).rjust(45) + " " + results. map{|s| s.ljust(5)}.join( " " ) } __END__ (0.05+0.01) ; 0.06 ; = (0.34+0.01) ; 0.35 ; = 0.0 ; 0.0 ; = 1e-15 ; 0.0 ; = 0.0 ; 1e-15 ; = 1.23456789e14 ; 1.234567892e14 ; ! 1.23456789e9 ; 1.234567892e9 ; ! 1.23456789e4 ; 1.234567892e4 ; ! 1.23456789e-1 ; 1.234567892e-1 ; ! 1.23456789e-6 ; 1.234567892e-6 ; ! 1.23456789e-11 ; 1.234567892e-11 ; ! 1.23456789e-16 ; 1.234567892e-16 ; ! 1.23456789012345e14 ; 1.234567890123452e14 ; = 1.23456789012345e9 ; 1.234567890123452e9 ; = 1.23456789012345e4 ; 1.234567890123452e4 ; = 1.23456789012345e-1 ; 1.234567890123452e-1 ; = 1.23456789012345e-6 ; 1.234567890123452e-6 ; = 1.23456789012345e-11 ; 1.234567890123452e-11 ; = 1.23456789012345e-16 ; 1.234567890123452e-16 ; = ==== output ==== (0.05+0.01) == 0.06 true true (0.34+0.01) == 0.35 true true 0.0 == 0.0 true true 1e-15 == 0.0 true true 0.0 == 1e-15 true true 1.23456789e14 == 1.234567892e14 false false 1.23456789e9 == 1.234567892e9 false false 1.23456789e4 == 1.234567892e4 false false 1.23456789e-1 == 1.234567892e-1 false false 1.23456789e-6 == 1.234567892e-6 false false 1.23456789e-11 == 1.234567892e-11 TRUE false 1.23456789e-16 == 1.234567892e-16 TRUE false 1.23456789012345e14 == 1.234567890123452e14 true true 1.23456789012345e9 == 1.234567890123452e9 true true 1.23456789012345e4 == 1.234567890123452e4 true true 1.23456789012345e-1 == 1.234567890123452e-1 true true 1.23456789012345e-6 == 1.234567890123452e-6 true true 1.23456789012345e-11 == 1.234567890123452e-11 true true 1.23456789012345e-16 == 1.234567890123452e-16 true true