In Files

  • rational.c
toggle debugging

Rational

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 or rationalize method. 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)

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 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)
{
    switch (TYPE(other)) {
      case T_FIXNUM:
      case T_BIGNUM:
        {
            get_dat1(self);

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

            return f_muldiv(self,
                            adat->num, adat->den,
                            bdat->num, bdat->den, '*');
        }
      default:
        return rb_num_coerce_bin(self, other, '*');
    }
}
            
rat ** numeric → numeric click to toggle source

Performs exponentiation.

Rational(2)    ** Rational(3)    #=> (8/1)
Rational(10)   ** -2             #=> (1/100)
Rational(10)   ** -2.0           #=> 0.01
Rational(-4)   ** Rational(1,2)  #=> (1.2246063538223773e-16+2.0i)
Rational(1, 2) ** 0              #=> (1/1)
Rational(1, 2) ** 0.0            #=> 1.0
 
               static VALUE
nurat_expt(VALUE self, VALUE other)
{
    if (k_numeric_p(other) && k_exact_zero_p(other))
        return f_rational_new_bang1(CLASS_OF(self), ONE);

    if (k_rational_p(other)) {
        get_dat1(other);

        if (f_one_p(dat->den))
            other = dat->num; /* c14n */
    }

    /* Deal with special cases of 0**n and 1**n */
    if (k_numeric_p(other) && k_exact_p(other)) {
        get_dat1(self);
        if (f_one_p(dat->den))
            if (f_one_p(dat->num))
                return f_rational_new_bang1(CLASS_OF(self), ONE);
            else if (f_minus_one_p(dat->num) && k_integer_p(other))
                return f_rational_new_bang1(CLASS_OF(self), INT2FIX(f_odd_p(other) ? -1 : 1));
            else if (f_zero_p(dat->num))
                if (FIX2INT(f_cmp(other, ZERO)) == -1)
                    rb_raise_zerodiv();
                else
                    return f_rational_new_bang1(CLASS_OF(self), ZERO);
    }

    /* General case */
    switch (TYPE(other)) {
      case T_FIXNUM:
        {
            VALUE num, den;

            get_dat1(self);

            switch (FIX2INT(f_cmp(other, ZERO))) {
              case 1:
                num = f_expt(dat->num, other);
                den = f_expt(dat->den, other);
                break;
              case -1:
                num = f_expt(dat->den, f_negate(other));
                den = f_expt(dat->num, f_negate(other));
                break;
              default:
                num = ONE;
                den = ONE;
                break;
            }
            return f_rational_new2(CLASS_OF(self), num, den);
        }
      case T_BIGNUM:
        rb_warn("in a**b, b may be too big");
        /* fall through */
      case T_FLOAT:
      case T_RATIONAL:
        return f_expt(f_to_f(self), other);
      default:
        return rb_num_coerce_bin(self, other, id_expt);
    }
}
            
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)
{
    switch (TYPE(other)) {
      case T_FIXNUM:
      case T_BIGNUM:
        {
            get_dat1(self);

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

            return f_addsub(self,
                            adat->num, adat->den,
                            bdat->num, bdat->den, '+');
        }
      default:
        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)
{
    switch (TYPE(other)) {
      case T_FIXNUM:
      case T_BIGNUM:
        {
            get_dat1(self);

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

            return f_addsub(self,
                            adat->num, adat->den,
                            bdat->num, bdat->den, '-');
        }
      default:
        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)
{
    switch (TYPE(other)) {
      case T_FIXNUM:
      case T_BIGNUM:
        if (f_zero_p(other))
            rb_raise_zerodiv();
        {
            get_dat1(self);

            return f_muldiv(self,
                            dat->num, dat->den,
                            other, ONE, '/');
        }
      case T_FLOAT:
        {
            double x = RFLOAT_VALUE(other), den;
            get_dat1(self);

            if (isnan(x)) return DBL2NUM(NAN);
            if (isinf(x)) return INT2FIX(0);
            if (x != 0.0 && modf(x, &den) == 0.0) {
                return rb_rational_raw2(dat->num, f_mul(rb_dbl2big(den), dat->den));
            }
        }
        return rb_funcall(f_to_f(self), '/', 1, other);
      case 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, '/');
        }
      default:
        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)
{
    switch (TYPE(other)) {
      case T_FIXNUM:
      case 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));
        }
      case T_FLOAT:
        return f_cmp(f_to_f(self), other);
      case 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);
        }
      default:
        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)
{
    switch (TYPE(other)) {
      case T_FIXNUM:
      case 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;
        }
      case T_FLOAT:
        return f_eqeq_p(f_to_f(self), other);
      case 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));
        }
      default:
        return f_eqeq_p(other, self);
    }
}
            
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)
{
    switch (TYPE(other)) {
      case T_FIXNUM:
      case T_BIGNUM:
        if (f_zero_p(other))
            rb_raise_zerodiv();
        {
            get_dat1(self);

            return f_muldiv(self,
                            dat->num, dat->den,
                            other, ONE, '/');
        }
      case T_FLOAT:
        {
            double x = RFLOAT_VALUE(other), den;
            get_dat1(self);

            if (isnan(x)) return DBL2NUM(NAN);
            if (isinf(x)) return INT2FIX(0);
            if (x != 0.0 && modf(x, &den) == 0.0) {
                return rb_rational_raw2(dat->num, f_mul(rb_dbl2big(den), dat->den));
            }
        }
        return rb_funcall(f_to_f(self), '/', 1, other);
      case 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, '/');
        }
      default:
        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_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_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);
}
            

Commenting is here to help enhance the documentation. For example, code samples, or clarification of the documentation.

If you have questions about Ruby or the documentation, please post to one of the Ruby mailing lists. You will get better, faster, help that way.

If you wish to post a correction of the docs, please do so, but also file bug report so that it can be corrected for the next release. Thank you.

If you want to help improve the Ruby documentation, please visit Documenting-ruby.org.

blog comments powered by Disqus