From: Eric Mahurin Date: 2005-05-06T21:22:44+09:00 Subject: Re: Float to Rational Here is another simple way I came up with. This shouldn't be any less accurate than the to_r you have since the error in the mantissa is at least 1 mantissa bit (I'm assuming 50 mantissa bits). The difference between this and what you had was you only multiplied by 2 forcing the denominator to be a power of 2 and you waited until the numerator was exactly an integer. 17.0/37 seems to be the float with the smallest denominator that starts giving problems in this algorithm. Another possibility would be to test incrementing/decrementing the numerator and/or denominator to see if you get a simpler rational. This should be valid since you know that mantissa error translates to numerator/denominator error. require 'mathn' class Float Err = 2.0**-50 def to_r num = self denominator = 1 loop do # factors needed to get denominators 2-36 (sometimes 37) [2,3,2,5,7,2,3,11,13,2,17,19,23,5,3,29,31,2,37].each do |f| if (1-num/(numerator=num.round)).abs<=Err return numerator/denominator end denominator *= f num *= f end end end end > require 'mathn' > > class Numeric > def inverse > 1/self > end > end > > class Float > > def to_r > n = 1 > n *= 2 until (self*n) % 1 == 0 > (self*n).to_i/n > end > > def round_to_r > return self.to_i if self % 1 == 0 > n = self > ops = [] > count = 0 > until ((n%1).round - n%1).abs < 1e-8 || count > 20 || > n.abs == 1.0/0.0 || n == 0.0/0.0 > int, dec = n.divmod 1 > ops.concat [[:+, int.to_i], [:inverse]] > n = 1/dec > count += 1 > end > n = n.round > ops.reverse.inject(n.round){|n, op| n.send(*op)} > end > end > ---- > > Use Float#to_r for an exact representation of the float > value, or > Float#round_to_r for an extremely close representation of it. > > cheers, > Mark > > Yahoo! Mail Stay connected, organized, and protected. Take the tour: http://tour.mail.yahoo.com/mailtour.html