Skip to content

Commit

Permalink
Avoid overhead of Float() and Rational() when numerator is guaranteed…
Browse files Browse the repository at this point in the history
… to not be one.
  • Loading branch information
CNU cnuapp account committed Jan 7, 2010
1 parent 3cb619c commit cf3972f
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 94 deletions.
182 changes: 89 additions & 93 deletions lib/rational.rb
Expand Up @@ -7,6 +7,8 @@
#
# Documentation by Kevin Jackson and Gavin Sinclair.
#
# Performance improvements by Kurt Stephens.
#
# When you <tt>require 'rational'</tt>, all interactions between numbers
# potentially return a rational result. For example:
#
Expand Down Expand Up @@ -104,13 +106,8 @@ def initialize(num, den)
num = -num
den = -den
end
if num.kind_of?(Integer) and den.kind_of?(Integer)
@numerator = num
@denominator = den
else
@numerator = num.to_i
@denominator = den.to_i
end
@numerator = num.to_i
@denominator = den.to_i
end

#
Expand All @@ -122,14 +119,13 @@ def initialize(num, den)
# r + 0.5 # -> 1.25
#
def + (a)
if a.kind_of?(Rational)
num = @numerator * a.denominator
num_a = a.numerator * @denominator
Rational(num + num_a, @denominator * a.denominator)
elsif a.kind_of?(Integer)
self + Rational.new!(a, 1)
elsif a.kind_of?(Float)
Float(self) + a
case a
when Rational # => Rational | Integer
Rational( (@numerator * a.denominator) + (a.numerator * @denominator), @denominator * a.denominator)
when Integer # => Rational
Rational.reduce((@numerator ) + (a * @denominator), @denominator)
when Float
self.to_f + a
else
x, y = a.coerce(self)
x + y
Expand All @@ -146,20 +142,26 @@ def + (a)
# r - 0.5 # -> 0.25
#
def - (a)
if a.kind_of?(Rational)
num = @numerator * a.denominator
num_a = a.numerator * @denominator
Rational(num - num_a, @denominator*a.denominator)
elsif a.kind_of?(Integer)
self - Rational.new!(a, 1)
elsif a.kind_of?(Float)
Float(self) - a
case a
when Rational # => Rational | Integer
Rational( (@numerator * a.denominator) - (a.numerator * @denominator), @denominator * a.denominator)
when Integer # => Rational
Rational.reduce((@numerator ) - (a * @denominator), @denominator)
when Float
self.to_f - a
else
x, y = a.coerce(self)
x - y
end
end

#
# Unary Minus--Returns the receiver's value, negated.
#
def -@
Rational.new!(- @numerator, @denominator)
end

#
# Returns the product of this value and +a+.
#
Expand All @@ -171,14 +173,13 @@ def - (a)
# r * Rational(1,2) # -> Rational(3,8)
#
def * (a)
if a.kind_of?(Rational)
num = @numerator * a.numerator
den = @denominator * a.denominator
Rational(num, den)
elsif a.kind_of?(Integer)
self * Rational.new!(a, 1)
elsif a.kind_of?(Float)
Float(self) * a
case a
when Rational
Rational(@numerator * a.numerator, @denominator * a.denominator)
when Integer
Rational(@numerator * a , @denominator)
when Float
self.to_f * a
else
x, y = a.coerce(self)
x * y
Expand All @@ -193,15 +194,14 @@ def * (a)
# r / Rational(1,2) # -> Rational(3,2)
#
def / (a)
if a.kind_of?(Rational)
num = @numerator * a.denominator
den = @denominator * a.numerator
Rational(num, den)
elsif a.kind_of?(Integer)
case a
when Rational
Rational(@numerator * a.denominator, @denominator * a.numerator)
when Integer
raise ZeroDivisionError, "division by zero" if a == 0
self / Rational.new!(a, 1)
elsif a.kind_of?(Float)
Float(self) / a
Rational(@numerator , @denominator * a )
when Float
self.to_f / a
else
x, y = a.coerce(self)
x / y
Expand All @@ -218,22 +218,17 @@ def / (a)
# r ** Rational(1,2) # -> 0.866025403784439
#
def ** (other)
if other.kind_of?(Rational)
Float(self) ** other
elsif other.kind_of?(Integer)
case other
when Rational, Float
self.to_f ** other
when Integer
if other > 0
num = @numerator ** other
den = @denominator ** other
Rational.new!(@numerator ** other, @denominator ** other)
elsif other < 0
num = @denominator ** -other
den = @numerator ** -other
elsif other == 0
num = 1
den = 1
Rational.new!(@denominator ** -other, @numerator ** -other)
else
Rational.new!(1, 1) # why not Fixnum 1?
end
Rational.new!(num, den)
elsif other.kind_of?(Float)
Float(self) ** other
else
x, y = other.coerce(self)
x ** y
Expand All @@ -256,7 +251,7 @@ def div(other)
#
def % (other)
value = (self / other).floor
return self - other * value
self - other * value
end

#
Expand All @@ -268,7 +263,7 @@ def % (other)
#
def divmod(other)
value = (self / other).floor
return value, self - other * value
[ value, self - other * value ]
end

#
Expand All @@ -282,6 +277,17 @@ def abs
end
end

# Returns true or false.
def zero?
@numerator.zero?
end

# See Numeric#nonzero?
def nonzero?
@numerator.nonzero? ? self : nil
end


#
# Returns +true+ iff this value is numerically equal to +other+.
#
Expand All @@ -292,12 +298,13 @@ def abs
# Don't use Rational.new!
#
def == (other)
if other.kind_of?(Rational)
case other
when Rational
@numerator == other.numerator and @denominator == other.denominator
elsif other.kind_of?(Integer)
self == Rational.new!(other, 1)
elsif other.kind_of?(Float)
Float(self) == other
when Integer
@numerator == other && @denominator == 1
when Float
self.to_f == other
else
other == self
end
Expand All @@ -307,34 +314,27 @@ def == (other)
# Standard comparison operator.
#
def <=> (other)
if other.kind_of?(Rational)
num = @numerator * other.denominator
num_a = other.numerator * @denominator
v = num - num_a
if v > 0
return 1
elsif v < 0
return -1
else
return 0
end
elsif other.kind_of?(Integer)
return self <=> Rational.new!(other, 1)
elsif other.kind_of?(Float)
return Float(self) <=> other
elsif defined? other.coerce
x, y = other.coerce(self)
return x <=> y
case other
when Rational
((@numerator * other.denominator) <=> (other.numerator * @denominator))
when Integer
((@numerator ) <=> (other * @denominator))
when Float
self.to_f <=> other
else
return nil
if defined? other.coerce
x, y = other.coerce(self)
x <=> y
end # nil
end
end

def coerce(other)
if other.kind_of?(Float)
return other, self.to_f
elsif other.kind_of?(Integer)
return Rational.new!(other, 1), self
case other
when Float
[ other, self.to_f ]
when Integer
[ Rational.new!(other, 1), self ]
else
super
end
Expand Down Expand Up @@ -363,23 +363,19 @@ def ceil()

def truncate()
if @numerator < 0
return -((-@numerator).div(@denominator))
-((-@numerator).div(@denominator))
else
@numerator.div(@denominator)
end
@numerator.div(@denominator)
end

alias_method :to_i, :truncate

def round()
if @numerator < 0
num = -@numerator
num = num * 2 + @denominator
den = @denominator * 2
-(num.div(den))
-((@numerator * -2 + @denominator).div(@denominator * 2))
else
num = @numerator * 2 + @denominator
den = @denominator * 2
num.div(den)
((@numerator * 2 + @denominator).div(@denominator * 2))
end
end

Expand All @@ -401,7 +397,7 @@ def to_s
if @denominator == 1
@numerator.to_s
else
@numerator.to_s+"/"+@denominator.to_s
"#{@numerator}/#{@denominator}"
end
end

Expand All @@ -418,7 +414,7 @@ def to_r
# Rational(5,8).inspect # -> "Rational(5, 8)"
#
def inspect
sprintf("Rational(%s, %s)", @numerator.inspect, @denominator.inspect)
"Rational(#{@numerator.inspect}, #{@denominator.inspect})"
end

#
Expand Down
25 changes: 25 additions & 0 deletions numeric.c
Expand Up @@ -3020,6 +3020,30 @@ fix_even_p(VALUE num)
return Qtrue;
}

static VALUE
fix_gcd(int argc, VALUE *argv, VALUE self) {
if ( argc != 1 ) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 1);
}
/* Handle Fixnum#gcd(Fixnum) here. */
if ( FIXNUM_P(argv[0]) ) {
/* fprintf(stderr, "Using Fixnum#gcd(Fixnum)\n"); */
long a = FIX2LONG(self);
long b = FIX2LONG(argv[0]);
long min = a < 0 ? - a : a;
long max = b < 0 ? - b : b;
while ( min > 0 ) {
int tmp = min;
min = max % min;
max = tmp;
}
return LONG2FIX(max);
} else {
/* Delegate to Integer#gcd. */
return rb_call_super(1, argv);
}
}

void
Init_Numeric()
{
Expand Down Expand Up @@ -3113,6 +3137,7 @@ Init_Numeric()
rb_define_method(rb_cFixnum, "divmod", fix_divmod, 1);
rb_define_method(rb_cFixnum, "quo", fix_quo, 1);
rb_define_method(rb_cFixnum, "fdiv", fix_quo, 1);
rb_define_method(rb_cFixnum, "gcd", fix_gcd, -1);
rb_define_method(rb_cFixnum, "**", fix_pow, 1);

rb_define_method(rb_cFixnum, "abs", fix_abs, 0);
Expand Down
2 changes: 1 addition & 1 deletion test/runner.rb
@@ -1,6 +1,6 @@
require 'test/unit'

rcsid = %w$Id$
rcsid = %w$Id: runner.rb 11708 2007-02-12 23:01:19Z shyouhei $ # git hack
Version = rcsid[2].scan(/\d+/).collect!(&method(:Integer)).freeze
Release = rcsid[3].freeze

Expand Down

0 comments on commit cf3972f

Please sign in to comment.