# approx.rb - =begin $kNotwork: approx.rb,v 1.26 2002/07/30 19:11:24 gotoken Exp $ = approx.rb == SYNOPSIS equality which allows IEEE 754 round off error. == EXAMPLE require "approx" p 0.3 == 0.1*3 #=> false p 0.3 === 0.1*3 #=> true p 2.0 == (2.0**0.5)**2.0 #=> false p 2.0 === (2.0**0.5)**2.0 #=> true p Float::MINIMUM === 0.0 #=> true p 0.0 === Float::MINIMUM #=> false == INSTANCE METHOD : Float#approximated_by?(x) : Float#approx?(x) : Float#===(x) tests |x - self| < two ulps (asymmetrically because ulp depends on magnitude of self). self and x have same sign if the value is true, e.g., 0.0.approximated_by?(-0.0) returns false, while 0.0 == -0.0. : Float#approximated_by?(x, delta) tests |x - self| < delta (symmetrically). `approx?' and `===' are synonyms for `approximated_by?'. : Float#approximated_by?(x, delta, true) tests |x - self| < delta and sign == x.sign (symmetrically). `approx?' and `===' are synonyms for `approximated_by?'. == REQUIRE (()) == AUTHORS Gotoken == COPYING approx.rb is copyrighted free software by GOTO Kentaro . You can redistribute it and/or modify it under the terms of Ruby's License (()). == LAST MODIFIED $Date$ =end __FILE__ == $0 and $:.unshift(File.dirname(__FILE__)) require "ieee754hack" APPROX_REIVISION = '$kNotwork: approx.rb,v 1.26 2002/07/30 19:11:24 gotoken Exp $' class Float def approximated_by?(x, delta = nil, sign_check = false) finite? and x.finite? and if delta if sign_check (x - self).abs < delta else sign == x.sign and (x - self).abs < delta end else sign == x.sign and (x - self).abs <= ulp end end alias_method(:===, :approximated_by?) alias_method(:approx?, :approximated_by?) end if __FILE__ == $0 print version_info = "# ruby #{VERSION} (#{RELEASE_DATE}) [#{PLATFORM}]\n" + "# #{File.basename($0)} #{APPROX_REIVISION}\n\n" def comment(src) text = src.gsub(/^(\s*?p\s(.*?)\s*?#=> ).*$/){$1 + eval($2).inspect} text != src and print "##### commented values in source are wrong! #####\n\n" print text end comment(<<-EOS) require "approx" # often confusing problems p 0.3 == 0.1*3 #=> false p 0.3 === 0.1*3 #=> true p 2.0 == (2**0.5)**2 #=> false p 2.0 === (2**0.5)**2 #=> true # ulp p 0.3.ulps_from(0.1*3) #=> -1 p 0.3.ulp #=> 5.551115123125783e-17 p 2.0.ulps_from((2**0.5)**2) #=> -1 p 2.0.ulp #=> 4.440892098500626e-16 # === is not wonder drug p 2.0 === (2**0.1)**(10) #=> false p 2.0.ulps_from((2**0.1)**10) #=> 3 # so recommended approx?() with 2nd arg (upper bound of permissible error) p 2.0.approx?((2.0**0.1)**(1/0.1), 1e-16) #=> false p 2.0.approx?((2.0**0.1)**(1/0.1), 1e-15) #=> true # asymmetricity p Float::MINIMUM === 0.0 #=> true p 0.0 === Float::MINIMUM #=> false # -0.0 == +0.0 ? p -0.0 == +0.0 #=> true p -0.0 === +0.0 #=> false p 1.0/-Math::exp(710) == +0.0 #=> true p 1.0/-Math::exp(710) === +0.0 #=> false EOS end