From: Mark Hubbart Date: 2005-10-19T18:18:46+09:00 Subject: Re: Integer#** weirdness On 10/17/05, Peter Vanbroekhoven wrote: > Hello, > > I've encountered a bit of a problem when I was trying out Ruby's numeric > coercion: > > class SomeNumberLikeThing > > attr :value > > def initialize(value) > @value = value > end > > def **(other) > SomeNumberLikeThing.new(value ** other.value) > end > > def coerce(other) > [SomeNumberLikeThing.new(other), self] > end > > end > > p 2.0 ** SomeNumberLikeThing.new(2) > # OK, returns # > p 2 ** SomeNumberLikeThing.new(2) > # ERROR: /home/peterv/local/lib/ruby/1.8/rational.rb:339:in `**': > undefined method `>=' for # @value=2> (NoMethodError) > > Seems that Integer#** tries to compare its argument to zero before > coercing. Problem is that I don't want this SomeNumberLikeThing to be > comparable. This is not something very exotic, as not all "number-like" > things are (totally) ordered, e.g., polynomials, matrices and complex > numbers. So IMO Integer#** should coerce before doing anything else with > its argument. Note that the error is generated in rational.rb. Rational.rb does the comparison so that it can trap negative exponents and give a rational result: irb> [2 ** -1, 2 ** -2] ==>[Rational(1, 2), Rational(1, 4)] Try not requiring rational.rb, and your code works perfectly. So this is actually a problem with the rational library. It appears that it does the comparison to gain speed when rational division isn't needed. A possible fix to this: change the Integer#rpower method definition in rational.rb from def rpower (other) if other >= 0 self.power!(other) else Rational.new!(self,1)**other end end to: def rpower (other) me, you = other.coerce(self) return me**you unless me == self if other >= 0 self.power!(other) else Rational.new!(self,1)**other end end This seems to fix your problem, while still letting Rational do its rational thing: p 2 ** SomeNumberLikeThing.new(2) p 2 ** SomeNumberLikeThing.new(-3) p 2 ** -3 # # Rational(1, 8) There seems to be a very small performance hit for this: mark@eMac% ruby -rbenchmark -rrational -e'Benchmark.bm(10){|x|x.report("modified"){100000.times{|n|2**(-n)}}}' user system total real modified 23.220000 0.400000 23.620000 ( 27.584483) mark@eMac% ruby -rbenchmark -rrational -e'Benchmark.bm(10){|x|x.report("original"){100000.times{|n|2**(-n)}}}' user system total real original 22.400000 0.400000 22.800000 ( 26.598195) ... but these benchmarks are not necessarily conclusive. I may be missing something big there. cheers, Mark > As a side note, Complex#<=> is defined, but it violates the definition of > an order: Complex(1, 1) >= Complex(1, -1) and Complex(1, 1) <= Complex(1, > -1) but not Complex(1, 1) == Complex(1, -1), which is a violation of the > antisymmetry law. > > Peter > >