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
endclass 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