class Rational

BigDecimal extends the native Rational class to provide the to_d method.

When you require BigDecimal in your application, this method will be available on Rational objects.

When mathn is required Rational is changed to simplify the use of Rational operations.

Normal behaviour:

Rational.new!(1,3) ** 2 # => Rational(1, 9)
(1 / 3) ** 2            # => 0

require 'mathn' behaviour:

(1 / 3) ** 2            # => 1/9

A rational number can be represented as a paired integer number; a/b (b>0). Where a is numerator and b is denominator. Integer a equals rational a/1 mathematically.

In ruby, you can create rational object with Rational, #to_r, rationalize method or suffixing r to a literal. The return values will be irreducible.

Rational(1)      #=> (1/1)
Rational(2, 3)   #=> (2/3)
Rational(4, -6)  #=> (-2/3)
3.to_r           #=> (3/1)
2/3r             #=> (2/3)

You can also create rational object from floating-point numbers or strings.

Rational(0.3)    #=> (5404319552844595/18014398509481984)
Rational('0.3')  #=> (3/10)
Rational('2/3')  #=> (2/3)

0.3.to_r         #=> (5404319552844595/18014398509481984)
'0.3'.to_r       #=> (3/10)
'2/3'.to_r       #=> (2/3)
0.3.rationalize  #=> (3/10)

A rational object is an exact number, which helps you to write program without any rounding errors.

10.times.inject(0){|t,| t + 0.1}              #=> 0.9999999999999999
10.times.inject(0){|t,| t + Rational('0.1')}  #=> (1/1)

However, when an expression has inexact factor (numerical value or operation), will produce an inexact result.

Rational(10) / 3   #=> (10/3)
Rational(10) / 3.0 #=> 3.3333333333333335

Rational(-8) ** Rational(1, 3)
                   #=> (1.0000000000000002+1.7320508075688772i)

Public Class Methods

json_create(object) click to toggle source
# File ext/json/lib/json/add/rational.rb, line 7
def self.json_create(object)
  Rational(object['n'], object['d'])
end

Public Instance Methods

rat * numeric → numeric click to toggle source

Performs multiplication.

Rational(2, 3)  * Rational(2, 3)   #=> (4/9)
Rational(900)   * Rational(1)      #=> (900/1)
Rational(-2, 9) * Rational(-9, 2)  #=> (1/1)
Rational(9, 8)  * 4                #=> (9/2)
Rational(20, 9) * 9.8              #=> 21.77777777777778
static VALUE
nurat_mul(VALUE self, VALUE other)
{
    if (RB_TYPE_P(other, T_FIXNUM) || RB_TYPE_P(other, T_BIGNUM)) {
        {
            get_dat1(self);

            return f_muldiv(self,
                            dat->num, dat->den,
                            other, ONE, '*');
        }
    }
    else if (RB_TYPE_P(other, T_FLOAT)) {
        return f_mul(f_to_f(self), other);
    }
    else if (RB_TYPE_P(other, T_RATIONAL)) {
        {
            get_dat2(self, other);

            return f_muldiv(self,
                            adat->num, adat->den,
                            bdat->num, bdat->den, '*');
        }
    }
    else {
        return rb_num_coerce_bin(self, other, '*');
    }
}
**(other) click to toggle source

Exponentiate by other

(1/3) ** 2 # => 1/9
# File lib/mathn.rb, line 138
def ** (other)
  if other.kind_of?(Rational)
    other2 = other
    if self < 0
      return Complex(self, 0.0) ** other
    elsif other == 0
      return Rational(1,1)
    elsif self == 0
      return Rational(0,1)
    elsif self == 1
      return Rational(1,1)
    end

    npd = numerator.prime_division
    dpd = denominator.prime_division
    if other < 0
      other = -other
      npd, dpd = dpd, npd
    end

    for elm in npd
      elm[1] = elm[1] * other
      if !elm[1].kind_of?(Integer) and elm[1].denominator != 1
        return Float(self) ** other2
      end
      elm[1] = elm[1].to_i
    end

    for elm in dpd
      elm[1] = elm[1] * other
      if !elm[1].kind_of?(Integer) and elm[1].denominator != 1
        return Float(self) ** other2
      end
      elm[1] = elm[1].to_i
    end

    num = Integer.from_prime_division(npd)
    den = Integer.from_prime_division(dpd)

    Rational(num,den)

  elsif other.kind_of?(Integer)
    if other > 0
      num = numerator ** other
      den = denominator ** other
    elsif other < 0
      num = denominator ** -other
      den = numerator ** -other
    elsif other == 0
      num = 1
      den = 1
    end
    Rational(num, den)
  elsif other.kind_of?(Float)
    Float(self) ** other
  else
    x , y = other.coerce(self)
    x ** y
  end
end
rat + numeric → numeric click to toggle source

Performs addition.

Rational(2, 3)  + Rational(2, 3)   #=> (4/3)
Rational(900)   + Rational(1)      #=> (900/1)
Rational(-2, 9) + Rational(-9, 2)  #=> (-85/18)
Rational(9, 8)  + 4                #=> (41/8)
Rational(20, 9) + 9.8              #=> 12.022222222222222
static VALUE
nurat_add(VALUE self, VALUE other)
{
    if (RB_TYPE_P(other, T_FIXNUM) || RB_TYPE_P(other, T_BIGNUM)) {
        {
            get_dat1(self);

            return f_addsub(self,
                            dat->num, dat->den,
                            other, ONE, '+');
        }
    }
    else if (RB_TYPE_P(other, T_FLOAT)) {
        return f_add(f_to_f(self), other);
    }
    else if (RB_TYPE_P(other, T_RATIONAL)) {
        {
            get_dat2(self, other);

            return f_addsub(self,
                            adat->num, adat->den,
                            bdat->num, bdat->den, '+');
        }
    }
    else {
        return rb_num_coerce_bin(self, other, '+');
    }
}
rat - numeric → numeric click to toggle source

Performs subtraction.

Rational(2, 3)  - Rational(2, 3)   #=> (0/1)
Rational(900)   - Rational(1)      #=> (899/1)
Rational(-2, 9) - Rational(-9, 2)  #=> (77/18)
Rational(9, 8)  - 4                #=> (23/8)
Rational(20, 9) - 9.8              #=> -7.577777777777778
static VALUE
nurat_sub(VALUE self, VALUE other)
{
    if (RB_TYPE_P(other, T_FIXNUM) || RB_TYPE_P(other, T_BIGNUM)) {
        {
            get_dat1(self);

            return f_addsub(self,
                            dat->num, dat->den,
                            other, ONE, '-');
        }
    }
    else if (RB_TYPE_P(other, T_FLOAT)) {
        return f_sub(f_to_f(self), other);
    }
    else if (RB_TYPE_P(other, T_RATIONAL)) {
        {
            get_dat2(self, other);

            return f_addsub(self,
                            adat->num, adat->den,
                            bdat->num, bdat->den, '-');
        }
    }
    else {
        return rb_num_coerce_bin(self, other, '-');
    }
}
rat / numeric → numeric click to toggle source

Performs division.

Rational(2, 3)  / Rational(2, 3)   #=> (1/1)
Rational(900)   / Rational(1)      #=> (900/1)
Rational(-2, 9) / Rational(-9, 2)  #=> (4/81)
Rational(9, 8)  / 4                #=> (9/32)
Rational(20, 9) / 9.8              #=> 0.22675736961451246
static VALUE
nurat_div(VALUE self, VALUE other)
{
    if (RB_TYPE_P(other, T_FIXNUM) || RB_TYPE_P(other, T_BIGNUM)) {
        if (f_zero_p(other))
            rb_raise_zerodiv();
        {
            get_dat1(self);

            return f_muldiv(self,
                            dat->num, dat->den,
                            other, ONE, '/');
        }
    }
    else if (RB_TYPE_P(other, T_FLOAT))
        return rb_funcall(f_to_f(self), '/', 1, other);
    else if (RB_TYPE_P(other, T_RATIONAL)) {
        if (f_zero_p(other))
            rb_raise_zerodiv();
        {
            get_dat2(self, other);

            if (f_one_p(self))
                return f_rational_new_no_reduce2(CLASS_OF(self),
                                                 bdat->den, bdat->num);

            return f_muldiv(self,
                            adat->num, adat->den,
                            bdat->num, bdat->den, '/');
        }
    }
    else {
        return rb_num_coerce_bin(self, other, '/');
    }
}
rational <=> numeric → -1, 0, +1 or nil click to toggle source

Performs comparison and returns -1, 0, or +1.

nil is returned if the two values are incomparable.

Rational(2, 3)  <=> Rational(2, 3)  #=> 0
Rational(5)     <=> 5               #=> 0
Rational(2,3)   <=> Rational(1,3)   #=> 1
Rational(1,3)   <=> 1               #=> -1
Rational(1,3)   <=> 0.3             #=> 1
static VALUE
nurat_cmp(VALUE self, VALUE other)
{
    if (RB_TYPE_P(other, T_FIXNUM) || RB_TYPE_P(other, T_BIGNUM)) {
        {
            get_dat1(self);

            if (FIXNUM_P(dat->den) && FIX2LONG(dat->den) == 1)
                return f_cmp(dat->num, other); /* c14n */
            return f_cmp(self, f_rational_new_bang1(CLASS_OF(self), other));
        }
    }
    else if (RB_TYPE_P(other, T_FLOAT)) {
        return f_cmp(f_to_f(self), other);
    }
    else if (RB_TYPE_P(other, T_RATIONAL)) {
        {
            VALUE num1, num2;

            get_dat2(self, other);

            if (FIXNUM_P(adat->num) && FIXNUM_P(adat->den) &&
                FIXNUM_P(bdat->num) && FIXNUM_P(bdat->den)) {
                num1 = f_imul(FIX2LONG(adat->num), FIX2LONG(bdat->den));
                num2 = f_imul(FIX2LONG(bdat->num), FIX2LONG(adat->den));
            }
            else {
                num1 = f_mul(adat->num, bdat->den);
                num2 = f_mul(bdat->num, adat->den);
            }
            return f_cmp(f_sub(num1, num2), ZERO);
        }
    }
    else {
        return rb_num_coerce_cmp(self, other, id_cmp);
    }
}
rat == object → true or false click to toggle source

Returns true if rat equals object numerically.

Rational(2, 3)  == Rational(2, 3)   #=> true
Rational(5)     == 5                #=> true
Rational(0)     == 0.0              #=> true
Rational('1/3') == 0.33             #=> false
Rational('1/2') == '1/2'            #=> false
static VALUE
nurat_eqeq_p(VALUE self, VALUE other)
{
    if (RB_TYPE_P(other, T_FIXNUM) || RB_TYPE_P(other, T_BIGNUM)) {
        {
            get_dat1(self);

            if (f_zero_p(dat->num) && f_zero_p(other))
                return Qtrue;

            if (!FIXNUM_P(dat->den))
                return Qfalse;
            if (FIX2LONG(dat->den) != 1)
                return Qfalse;
            if (f_eqeq_p(dat->num, other))
                return Qtrue;
            return Qfalse;
        }
    }
    else if (RB_TYPE_P(other, T_FLOAT)) {
        return f_eqeq_p(f_to_f(self), other);
    }
    else if (RB_TYPE_P(other, T_RATIONAL)) {
        {
            get_dat2(self, other);

            if (f_zero_p(adat->num) && f_zero_p(bdat->num))
                return Qtrue;

            return f_boolcast(f_eqeq_p(adat->num, bdat->num) &&
                              f_eqeq_p(adat->den, bdat->den));
        }
    }
    else {
        return f_eqeq_p(other, self);
    }
}
as_json(*) click to toggle source
# File ext/json/lib/json/add/rational.rb, line 11
def as_json(*)
  {
    JSON.create_id => self.class.name,
    'n'            => numerator,
    'd'            => denominator,
  }
end
ceil → integer click to toggle source
ceil(precision=0) → rational

Returns the truncated value (toward positive infinity).

Rational(3).ceil      #=> 3
Rational(2, 3).ceil   #=> 1
Rational(-3, 2).ceil  #=> -1

       decimal      -  1  2  3 . 4  5  6
                      ^  ^  ^  ^   ^  ^
      precision      -3 -2 -1  0  +1 +2

'%f' % Rational('-123.456').ceil(+1)  #=> "-123.400000"
'%f' % Rational('-123.456').ceil(-1)  #=> "-120.000000"
static VALUE
nurat_ceil_n(int argc, VALUE *argv, VALUE self)
{
    return f_round_common(argc, argv, self, nurat_ceil);
}
denominator → integer click to toggle source

Returns the denominator (always positive).

Rational(7).denominator             #=> 1
Rational(7, 1).denominator          #=> 1
Rational(9, -4).denominator         #=> 4
Rational(-2, -10).denominator       #=> 5
rat.numerator.gcd(rat.denominator)  #=> 1
static VALUE
nurat_denominator(VALUE self)
{
    get_dat1(self);
    return dat->den;
}
fdiv(numeric) → float click to toggle source

Performs division and returns the value as a float.

Rational(2, 3).fdiv(1)       #=> 0.6666666666666666
Rational(2, 3).fdiv(0.5)     #=> 1.3333333333333333
Rational(2).fdiv(3)          #=> 0.6666666666666666
static VALUE
nurat_fdiv(VALUE self, VALUE other)
{
    if (f_zero_p(other))
        return f_div(self, f_to_f(other));
    return f_to_f(f_div(self, other));
}
floor → integer click to toggle source
floor(precision=0) → rational

Returns the truncated value (toward negative infinity).

Rational(3).floor      #=> 3
Rational(2, 3).floor   #=> 0
Rational(-3, 2).floor  #=> -1

       decimal      -  1  2  3 . 4  5  6
                      ^  ^  ^  ^   ^  ^
      precision      -3 -2 -1  0  +1 +2

'%f' % Rational('-123.456').floor(+1)  #=> "-123.500000"
'%f' % Rational('-123.456').floor(-1)  #=> "-130.000000"
static VALUE
nurat_floor_n(int argc, VALUE *argv, VALUE self)
{
    return f_round_common(argc, argv, self, nurat_floor);
}
inspect → string click to toggle source

Returns the value as a string for inspection.

Rational(2).inspect      #=> "(2/1)"
Rational(-8, 6).inspect  #=> "(-4/3)"
Rational('1/2').inspect  #=> "(1/2)"
static VALUE
nurat_inspect(VALUE self)
{
    VALUE s;

    s = rb_usascii_str_new2("(");
    rb_str_concat(s, f_format(self, f_inspect));
    rb_str_cat2(s, ")");

    return s;
}
numerator → integer click to toggle source

Returns the numerator.

Rational(7).numerator        #=> 7
Rational(7, 1).numerator     #=> 7
Rational(9, -4).numerator    #=> -9
Rational(-2, -10).numerator  #=> 1
static VALUE
nurat_numerator(VALUE self)
{
    get_dat1(self);
    return dat->num;
}
quo(numeric) → numeric click to toggle source

Performs division.

Rational(2, 3)  / Rational(2, 3)   #=> (1/1)
Rational(900)   / Rational(1)      #=> (900/1)
Rational(-2, 9) / Rational(-9, 2)  #=> (4/81)
Rational(9, 8)  / 4                #=> (9/32)
Rational(20, 9) / 9.8              #=> 0.22675736961451246
static VALUE
nurat_div(VALUE self, VALUE other)
{
    if (RB_TYPE_P(other, T_FIXNUM) || RB_TYPE_P(other, T_BIGNUM)) {
        if (f_zero_p(other))
            rb_raise_zerodiv();
        {
            get_dat1(self);

            return f_muldiv(self,
                            dat->num, dat->den,
                            other, ONE, '/');
        }
    }
    else if (RB_TYPE_P(other, T_FLOAT))
        return rb_funcall(f_to_f(self), '/', 1, other);
    else if (RB_TYPE_P(other, T_RATIONAL)) {
        if (f_zero_p(other))
            rb_raise_zerodiv();
        {
            get_dat2(self, other);

            if (f_one_p(self))
                return f_rational_new_no_reduce2(CLASS_OF(self),
                                                 bdat->den, bdat->num);

            return f_muldiv(self,
                            adat->num, adat->den,
                            bdat->num, bdat->den, '/');
        }
    }
    else {
        return rb_num_coerce_bin(self, other, '/');
    }
}
rationalize → self click to toggle source
rationalize(eps) → rational

Returns a simpler approximation of the value if the optional argument eps is given (rat-|eps| <= result <= rat+|eps|), self otherwise.

r = Rational(5033165, 16777216)
r.rationalize                    #=> (5033165/16777216)
r.rationalize(Rational('0.01'))  #=> (3/10)
r.rationalize(Rational('0.1'))   #=> (1/3)
static VALUE
nurat_rationalize(int argc, VALUE *argv, VALUE self)
{
    VALUE e, a, b, p, q;

    if (argc == 0)
        return self;

    if (f_negative_p(self))
        return f_negate(nurat_rationalize(argc, argv, f_abs(self)));

    rb_scan_args(argc, argv, "01", &e);
    e = f_abs(e);
    a = f_sub(self, e);
    b = f_add(self, e);

    if (f_eqeq_p(a, b))
        return self;

    nurat_rationalize_internal(a, b, &p, &q);
    return f_rational_new2(CLASS_OF(self), p, q);
}
round → integer click to toggle source
round(precision=0) → rational

Returns the truncated value (toward the nearest integer; 0.5 => 1; -0.5 => -1).

Rational(3).round      #=> 3
Rational(2, 3).round   #=> 1
Rational(-3, 2).round  #=> -2

       decimal      -  1  2  3 . 4  5  6
                      ^  ^  ^  ^   ^  ^
      precision      -3 -2 -1  0  +1 +2

'%f' % Rational('-123.456').round(+1)  #=> "-123.500000"
'%f' % Rational('-123.456').round(-1)  #=> "-120.000000"
static VALUE
nurat_round_n(int argc, VALUE *argv, VALUE self)
{
    return f_round_common(argc, argv, self, nurat_round);
}
to_d(precision) → bigdecimal click to toggle source

Converts a Rational to a BigDecimal.

The required precision parameter is used to determine the amount of significant digits for the result. See BigDecimal#div for more information, as it is used along with the denominator and the precision for parameters.

r = (22/7.0).to_r
# => (7077085128725065/2251799813685248)
r.to_d(3)
# => #<BigDecimal:1a44d08,'0.314E1',18(36)>
# File ext/bigdecimal/lib/bigdecimal/util.rb, line 120
def to_d(precision)
  if precision <= 0
    raise ArgumentError, "negative precision"
  end
  num = self.numerator
  BigDecimal(num).div(self.denominator, precision)
end
to_f → float click to toggle source

Return the value as a float.

Rational(2).to_f      #=> 2.0
Rational(9, 4).to_f   #=> 2.25
Rational(-3, 4).to_f  #=> -0.75
Rational(20, 3).to_f  #=> 6.666666666666667
static VALUE
nurat_to_f(VALUE self)
{
    get_dat1(self);
    return f_fdiv(dat->num, dat->den);
}
to_i → integer click to toggle source

Returns the truncated value as an integer.

Equivalent to

rat.truncate.

Rational(2, 3).to_i   #=> 0
Rational(3).to_i      #=> 3
Rational(300.6).to_i  #=> 300
Rational(98,71).to_i  #=> 1
Rational(-30,2).to_i  #=> -15
static VALUE
nurat_truncate(VALUE self)
{
    get_dat1(self);
    if (f_negative_p(dat->num))
        return f_negate(f_idiv(f_negate(dat->num), dat->den));
    return f_idiv(dat->num, dat->den);
}
to_json(*) click to toggle source
# File ext/json/lib/json/add/rational.rb, line 19
def to_json(*)
  as_json.to_json
end
to_r → self click to toggle source

Returns self.

Rational(2).to_r      #=> (2/1)
Rational(-8, 6).to_r  #=> (-4/3)
static VALUE
nurat_to_r(VALUE self)
{
    return self;
}
to_s → string click to toggle source

Returns the value as a string.

Rational(2).to_s      #=> "2/1"
Rational(-8, 6).to_s  #=> "-4/3"
Rational('1/2').to_s  #=> "1/2"
static VALUE
nurat_to_s(VALUE self)
{
    return f_format(self, f_to_s);
}
truncate → integer click to toggle source
truncate(precision=0) → rational

Returns the truncated value (toward zero).

Rational(3).truncate      #=> 3
Rational(2, 3).truncate   #=> 0
Rational(-3, 2).truncate  #=> -1

       decimal      -  1  2  3 . 4  5  6
                      ^  ^  ^  ^   ^  ^
      precision      -3 -2 -1  0  +1 +2

'%f' % Rational('-123.456').truncate(+1)  #=>  "-123.400000"
'%f' % Rational('-123.456').truncate(-1)  #=>  "-120.000000"
static VALUE
nurat_truncate_n(int argc, VALUE *argv, VALUE self)
{
    return f_round_common(argc, argv, self, nurat_truncate);
}