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