irb(main):001:0> include Math
=> Object
irb(main):002:0> sqrt(Rational(16,9))
=> 1.3333333333333333
I was able to correct this as follows:
include Math
class Integer
def square?
self >= 0 && sqrt(self).round ** 2 == self
end
end
module Math
alias sqrt sqrt
def sqrt(x)
if x.instance_of?(Rational)
n = x.numerator
d = x.denominator
return Rational(sqrt(n), sqrt(d)) if n.square? && d.square?
end sqrt x
end
end
I’m only butting in here because I’m genuinely interested; how
precisely is it broken? I read through Class: Rational (Ruby 1.9.3) and I can’t see
anything in Dan’s patched Math#sqrt() that defies anything in the
Rational() docs. Unless I’m not reading the right docs?
Further from what I can see about how Rational reduces floating point
numbers to ratios of integers, checking that the parts of the rational
are perfect squares isn’t necessary as the rationalised form has the
same precision as a floating point number. However in terms of
pretty,
Math.sqrt(Rational(16,9)) # => (4/3) is nice, but
Math.sqrt(Rational(17,9)) # => (1547401413261741/1125899906842624)
is much uglier (to me) than 1.3743685418725535
I forgot module_function – this is needed when modifying Math
functions to have “include Math” work correctly.
It uses “kind_of?” instead of “instance_of?”. This allows
compatibility with sub-classes or Rational. I suppose
“x.responds_to?(:numerator) && x.responds_to?(:denominator)” would also
have worked, but I’m not sure if I’m a huge fan of “duck typing”, since
it isn’t obvious that every class which defines these functions is using
them in a compatible fashion. “kind_of?” is safer.
mathn solves the square root of integer problem ( sqrt(16) = 4, not
sqrt(16) = 4.0), then redefines division of integers to produce a
Rational (if not an exact integer), then simply uses square root on the
numerator and denominator of the Rational. So it’s more comprehensive.
So the answer is: if you want these things to be well handled then use
“mathn”, albeit at the expense of speed.