Sqrt of rational numbers

I was a bit surprised when I ran the following

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

Now it works:

irb(main):032:0> sqrt(Rational(16,9))
=> (4/3)
irb(main):033:0> sqrt(Rational(16,8))
=> 1.4142135623730951

Dan C. wrote in post #1064519:

I was a bit surprised when I ran the following

Now it works:

No. You broke it. Read the Rational() docs.

Dan C. wrote in post #1064519:

I was a bit surprised when I ran the following

Now it works:

On 14 June 2012 14:33, Adam Ms. [email protected] wrote:

No. You broke it. Read the Rational() docs.

I’m only butting in here because I’m genuinely interested; how
precisely is it broken? I read through
http://www.ruby-doc.org/core-1.9.3/Rational.html 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


Matthew K., B.Sc (CompSci) (Hons)
http://matthew.kerwin.net.au/
ABN: 59-013-727-651

“You’ll never find a programming language that frees
you from the burden of clarifying your ideas.” - xkcd

Looking at mathn.rb was instructive.

  1. I forgot module_function – this is needed when modifying Math
    functions to have “include Math” work correctly.
  2. 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.
  3. 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.

Hmmm…
I notice, though, if I get rationals via mathn, I do get what I want
(w/o patching):

irb(main):002:0> Math.sqrt(Rational(16,9))
=> 1.3333333333333333
irb(main):003:0> require ‘mathn’
=> true
irb(main):004:0> Math.sqrt(Rational(16,9))
=> (4/3)
irb(main):005:0> Math.sqrt(16/9)
=> (4/3)