класс Rational
BigDecimal расширяет базовый класс Rational, чтобы предоставить метод to_d.
Когда в вашем приложении требуется BigDecimal, этот метод будет доступен для объектов Rational.
Рациональное число может быть представлено как пара целых чисел; a/b (b>0). Где a - числитель, а b - знаменатель. Целое число a равно рациональному числу a/1 математически.
В Ruby вы можете создать объект Rational с помощью Rational, #to_r, метода rationalize или добавив суффикс r к литералу. Результаты будут приведены к несократимому виду.
Rational(1) #=> (1/1) Rational(2, 3) #=> (2/3) Rational(4, -6) #=> (-2/3) 3.to_r #=> (3/1) 2/3r #=> (2/3)
Вы также можете создать объект Rational из чисел с плавающей точкой или строк.
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)
Объект Rational является точным числом, что помогает вам писать программы без округления.
10.times.inject(0){|t,| t + 0.1} #=> 0.9999999999999999
10.times.inject(0){|t,| t + Rational('0.1')} #=> (1/1)
Однако, когда выражение содержит неточные факторы (числовые значения или операции), результатом будет неточное значение.
Rational(10) / 3 #=> (10/3)
Rational(10) / 3.0 #=> 3.3333333333333335
Rational(-8) ** Rational(1, 3)
#=> (1.0000000000000002+1.7320508075688772i)
Методы публичного класса
Методы публичного экземпляра
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, '*');
}
} Выполняет умножение.
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_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 */
if (RB_TYPE_P(other, 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;
}
if (RB_FLOAT_TYPE_P(num)) { /* infinity due to overflow */
if (RB_FLOAT_TYPE_P(den)) return DBL2NUM(NAN);
return num;
}
if (RB_FLOAT_TYPE_P(den)) { /* infinity due to overflow */
num = ZERO;
den = ONE;
}
return f_rational_new2(CLASS_OF(self), num, den);
}
}
else if (RB_TYPE_P(other, T_BIGNUM)) {
rb_warn("in a**b, b may be too big");
return f_expt(f_to_f(self), other);
}
else if (RB_TYPE_P(other, T_FLOAT) || RB_TYPE_P(other, T_RATIONAL)) {
return f_expt(f_to_f(self), other);
}
else {
return rb_num_coerce_bin(self, other, id_expt);
}
} Выполняет возведение в степень.
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_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, '+');
}
} Выполняет сложение.
Rational(2, 3) + Rational(2, 3) #=> (4/3) Rational(900) + Rational(1) #=> (901/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_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, '-');
}
} Выполняет вычитание.
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_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(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_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);
}
} Выполняет сравнение и возвращает -1, 0 или +1.
Возвращается nil, если два значения несравнимы.
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_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);
}
} Возвращает true, если rat численно равно object.
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
# File ext/json/lib/json/add/rational.rb, line 16
def as_json(*)
{
JSON.create_id => self.class.name,
'n' => numerator,
'd' => denominator,
}
end Возвращает хеш, который будет преобразован в объект JSON и представлять этот объект.
static VALUE
nurat_ceil_n(int argc, VALUE *argv, VALUE self)
{
return f_round_common(argc, argv, self, nurat_ceil);
} Возвращает усеченное значение (в сторону положительной бесконечности).
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_denominator(VALUE self)
{
get_dat1(self);
return dat->den;
} Возвращает знаменатель (всегда положительный).
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_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));
} Выполняет деление и возвращает значение как число с плавающей точкой.
Rational(2, 3).fdiv(1) #=> 0.6666666666666666 Rational(2, 3).fdiv(0.5) #=> 1.3333333333333333 Rational(2).fdiv(3) #=> 0.6666666666666666
static VALUE
nurat_floor_n(int argc, VALUE *argv, VALUE self)
{
return f_round_common(argc, argv, self, nurat_floor);
} Возвращает усеченное значение (в сторону отрицательной бесконечности).
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_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;
} Возвращает значение в виде строки для проверки.
Rational(2).inspect #=> "(2/1)"
Rational(-8, 6).inspect #=> "(-4/3)"
Rational('1/2').inspect #=> "(1/2)"
static VALUE
nurat_numerator(VALUE self)
{
get_dat1(self);
return dat->num;
} Возвращает числитель.
Rational(7).numerator #=> 7 Rational(7, 1).numerator #=> 7 Rational(9, -4).numerator #=> -9 Rational(-2, -10).numerator #=> 1
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(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_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);
} Возвращает более простое приближение значения, если задан необязательный аргумент eps (rat-|eps| <= результат <= rat+|eps|), иначе self.
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_round_n(int argc, VALUE *argv, VALUE self)
{
return f_round_common(argc, argv, self, nurat_round);
} Возвращает усеченное значение (к ближайшему целому; 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"
# File ext/bigdecimal/lib/bigdecimal/util.rb, line 121
def to_d(precision)
if precision <= 0
raise ArgumentError, "negative precision"
end
num = self.numerator
BigDecimal(num).div(self.denominator, precision)
end Преобразует Rational в BigDecimal.
Необходимый параметр precision, используется для определения количества значащих цифр для результата. См. BigDecimal#div для получения более подробной информации, так как он используется вместе со значением denominator и параметром precision.
r = (22/7.0).to_r # => (7077085128725065/2251799813685248) r.to_d(3) # => #<BigDecimal:1a44d08,'0.314E1',18(36)>
static VALUE
nurat_to_f(VALUE self)
{
get_dat1(self);
return f_fdiv(dat->num, dat->den);
} Возвращает значение как число с плавающей точкой.
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_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);
} Возвращает усеченное значение как целое число.
Эквивалентно #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
# File ext/json/lib/json/add/rational.rb, line 25 def to_json(*) as_json.to_json end
Сохраняет имя класса (Rational) вместе со значением числителя n, значением знаменателя d в виде строки JSON.
static VALUE
nurat_to_r(VALUE self)
{
return self;
} Возвращает self.
Rational(2).to_r #=> (2/1) Rational(-8, 6).to_r #=> (-4/3)
static VALUE
nurat_to_s(VALUE self)
{
return f_format(self, f_to_s);
} Возвращает значение в виде строки.
Rational(2).to_s #=> "2/1"
Rational(-8, 6).to_s #=> "-4/3"
Rational('1/2').to_s #=> "1/2"
static VALUE
nurat_truncate_n(int argc, VALUE *argv, VALUE self)
{
return f_round_common(argc, argv, self, nurat_truncate);
} Возвращает усеченное значение (в сторону нуля).
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"
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.