class Rational
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 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 Class Methods
# File ext/json/lib/json/add/rational.rb, line 7 def self.json_create(object) Rational(object['n'], object['d']) end
Public Instance Methods
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, '*'); } }
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
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, '+'); } }
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, '-'); } }
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, '/'); } }
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); } }
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); } }
# File ext/json/lib/json/add/rational.rb, line 11 def as_json(*) { JSON.create_id => self.class.name, 'n' => numerator, 'd' => denominator, } end
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); }
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; }
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)); }
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); }
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; }
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; }
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, '/'); } }
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); }
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); }
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 99 def to_d(precision) if precision <= 0 raise ArgumentError, "negative precision" end num = self.numerator BigDecimal(num).div(self.denominator, precision) end
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); }
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); }
# File ext/json/lib/json/add/rational.rb, line 19 def to_json(*) as_json.to_json end
Returns self.
Rational(2).to_r #=> (2/1) Rational(-8, 6).to_r #=> (-4/3)
static VALUE nurat_to_r(VALUE self) { return self; }
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); }
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); }