класс Numeric
Базовый класс чисел.
Методы публичного экземпляра
static VALUE
num_modulo(VALUE x, VALUE y)
{
return rb_funcall(x, '-', 1,
rb_funcall(y, '*', 1,
rb_funcall(x, rb_intern("div"), 1, y)));
} x.modulo(y) means x-y*(x/y).floor
Эквивалентно num.divmod(numeric)[1].
См. #divmod.
static VALUE
num_uplus(VALUE num)
{
return num;
} Унарный плюс — возвращает значение получателя.
static VALUE
num_uminus(VALUE num)
{
VALUE zero;
zero = INT2FIX(0);
do_coerce(&zero, &num, TRUE);
return rb_funcall(zero, '-', 1, num);
} Унарный минус — возвращает значение получателя, с обратным знаком.
static VALUE
num_cmp(VALUE x, VALUE y)
{
if (x == y) return INT2FIX(0);
return Qnil;
} Возвращает ноль, если number равно other, в противном случае возвращает nil, если два значения несопоставимы.
static VALUE
num_abs(VALUE num)
{
if (negative_int_p(num)) {
return rb_funcall(num, rb_intern("-@"), 0);
}
return num;
} Возвращает абсолютное значение num.
12.abs #=> 12 (-34.56).abs #=> 34.56 -34.56.abs #=> 34.56
#magnitude — псевдоним для #abs.
static VALUE
numeric_abs2(VALUE self)
{
return f_mul(self, self);
} Возвращает квадрат самого себя.
static VALUE
numeric_arg(VALUE self)
{
if (f_positive_p(self))
return INT2FIX(0);
return rb_const_get(rb_mMath, id_PI);
} Возвращает 0, если значение положительное, иначе пи.
static VALUE
numeric_arg(VALUE self)
{
if (f_positive_p(self))
return INT2FIX(0);
return rb_const_get(rb_mMath, id_PI);
} Возвращает 0, если значение положительное, иначе пи.
static VALUE
num_ceil(VALUE num)
{
return flo_ceil(rb_Float(num));
} Возвращает наименьшее возможное целое число, которое больше или равно num.
Numeric достигает этого, преобразуя себя в Float, а затем вызывая Float#ceil.
1.ceil #=> 1 1.2.ceil #=> 2 (-1.2).ceil #=> -1 (-1.0).ceil #=> -1
static VALUE
num_coerce(VALUE x, VALUE y)
{
if (CLASS_OF(x) == CLASS_OF(y))
return rb_assoc_new(y, x);
x = rb_Float(x);
y = rb_Float(y);
return rb_assoc_new(y, x);
} Если тип +numeric совпадает с типом num, возвращает массив, содержащий numeric и num. В противном случае возвращает массив, содержащий и numeric и num, представленные как объекты Float.
Этот механизм приведения типов используется Ruby для обработки операций с числами разных типов: он предназначен для нахождения совместимого общего типа между двумя операндами оператора.
1.coerce(2.5) #=> [2.5, 1.0] 1.2.coerce(3) #=> [3.0, 1.2] 1.coerce(2) #=> [2, 1]
static VALUE
numeric_conj(VALUE self)
{
return self;
} Возвращает self.
static VALUE
numeric_conj(VALUE self)
{
return self;
} Возвращает self.
static VALUE
numeric_denominator(VALUE self)
{
return f_denominator(f_to_r(self));
} Возвращает знаменатель (всегда положительный).
static VALUE
num_div(VALUE x, VALUE y)
{
if (rb_equal(INT2FIX(0), y)) rb_num_zerodiv();
return rb_funcall(rb_funcall(x, '/', 1, y), rb_intern("floor"), 0);
} Использует / для выполнения деления, а затем преобразует результат в целое число. numeric не определяет оператор /; это остается на усмотрение подклассов.
Эквивалентно num.divmod(numeric)[0].
См. #divmod.
static VALUE
num_divmod(VALUE x, VALUE y)
{
return rb_assoc_new(num_div(x, y), num_modulo(x, y));
} Возвращает массив, содержащий частное и остаток от деления num на numeric.
Если q, r = * x.divmod(y), то
q = floor(x/y) x = q*y+r
Частное округляется к -бесконечности, как показано в следующей таблице:
a | b | a.divmod(b) | a/b | a.modulo(b) | a.remainder(b) ------+-----+---------------+---------+-------------+--------------- 13 | 4 | 3, 1 | 3 | 1 | 1 ------+-----+---------------+---------+-------------+--------------- 13 | -4 | -4, -3 | -4 | -3 | 1 ------+-----+---------------+---------+-------------+--------------- -13 | 4 | -4, 3 | -4 | 3 | -1 ------+-----+---------------+---------+-------------+--------------- -13 | -4 | 3, -1 | 3 | -1 | -1 ------+-----+---------------+---------+-------------+--------------- 11.5 | 4 | 2, 3.5 | 2.875 | 3.5 | 3.5 ------+-----+---------------+---------+-------------+--------------- 11.5 | -4 | -3, -0.5 | -2.875 | -0.5 | 3.5 ------+-----+---------------+---------+-------------+--------------- -11.5 | 4 | -3, 0.5 | -2.875 | 0.5 | -3.5 ------+-----+---------------+---------+-------------+--------------- -11.5 | -4 | 2, -3.5 | 2.875 | -3.5 | -3.5
Примеры
11.divmod(3) #=> [3, 2] 11.divmod(-3) #=> [-4, -1] 11.divmod(3.5) #=> [3, 0.5] (-11).divmod(3.5) #=> [-4, 3.0] (11.5).divmod(3.5) #=> [3, 1.0]
static VALUE
num_eql(VALUE x, VALUE y)
{
if (TYPE(x) != TYPE(y)) return Qfalse;
return rb_equal(x, y);
} Возвращает true, если num и numeric имеют одинаковый тип и равны по значению.
1 == 1.0 #=> true 1.eql?(1.0) #=> false (1.0).eql?(1.0) #=> true
static VALUE
num_fdiv(VALUE x, VALUE y)
{
return rb_funcall(rb_Float(x), '/', 1, y);
} Возвращает результат деления с плавающей запятой.
static VALUE
num_floor(VALUE num)
{
return flo_floor(rb_Float(num));
} Возвращает наибольшее целое число, меньшее или равное num.
Numeric реализует это, преобразуя Integer в Float и вызывая Float#floor.
1.floor #=> 1 (-1).floor #=> -1
static VALUE
num_imaginary(VALUE num)
{
return rb_complex_new(INT2FIX(0), num);
} Возвращает соответствующее мнимое число. Недоступно для комплексных чисел.
static VALUE
numeric_imag(VALUE self)
{
return INT2FIX(0);
} Возвращает ноль.
static VALUE
numeric_imag(VALUE self)
{
return INT2FIX(0);
} Возвращает ноль.
static VALUE
num_abs(VALUE num)
{
if (negative_int_p(num)) {
return rb_funcall(num, rb_intern("-@"), 0);
}
return num;
} Возвращает абсолютное значение num.
12.abs #=> 12 (-34.56).abs #=> 34.56 -34.56.abs #=> 34.56
#magnitude — псевдоним для #abs.
static VALUE
num_modulo(VALUE x, VALUE y)
{
return rb_funcall(x, '-', 1,
rb_funcall(y, '*', 1,
rb_funcall(x, rb_intern("div"), 1, y)));
} x.modulo(y) means x-y*(x/y).floor
Эквивалентно num.divmod(numeric)[1].
См. #divmod.
static VALUE
num_nonzero_p(VALUE num)
{
if (RTEST(rb_funcall(num, rb_intern("zero?"), 0, 0))) {
return Qnil;
}
return num;
} Возвращает self, если num не равно нулю, nil в противном случае.
Это поведение полезно при объединении сравнений:
a = %w( z Bb bB bb BB a aA Aa AA A )
b = a.sort {|a,b| (a.downcase <=> b.downcase).nonzero? || a <=> b }
b #=> ["A", "a", "AA", "Aa", "aA", "BB", "Bb", "bB", "bb", "z"]
static VALUE
numeric_numerator(VALUE self)
{
return f_numerator(f_to_r(self));
} Возвращает числитель.
static VALUE
numeric_arg(VALUE self)
{
if (f_positive_p(self))
return INT2FIX(0);
return rb_const_get(rb_mMath, id_PI);
} Возвращает 0, если значение положительно, иначе pi.
static VALUE
numeric_polar(VALUE self)
{
return rb_assoc_new(f_abs(self), f_arg(self));
} Возвращает массив; [num.abs, num.arg].
static VALUE
numeric_quo(VALUE x, VALUE y)
{
if (RB_TYPE_P(y, T_FLOAT)) {
return f_fdiv(x, y);
}
#ifdef CANON
if (canonicalization) {
x = rb_rational_raw1(x);
}
else
#endif
{
x = rb_convert_type(x, T_RATIONAL, "Rational", "to_r");
}
return rb_funcall(x, '/', 1, y);
} Возвращает наиболее точное деление (рациональное для целых чисел, число с плавающей точкой для чисел с плавающей точкой).
static VALUE
numeric_real(VALUE self)
{
return self;
} Возвращает self.
static VALUE
num_real_p(VALUE num)
{
return Qtrue;
} Возвращает true, если num является вещественным числом. (т.е. не Complex).
static VALUE
numeric_rect(VALUE self)
{
return rb_assoc_new(self, INT2FIX(0));
} Возвращает массив; [num, 0].
static VALUE
numeric_rect(VALUE self)
{
return rb_assoc_new(self, INT2FIX(0));
} Возвращает массив; [num, 0].
static VALUE
num_remainder(VALUE x, VALUE y)
{
VALUE z = rb_funcall(x, '%', 1, y);
if ((!rb_equal(z, INT2FIX(0))) &&
((negative_int_p(x) &&
positive_int_p(y)) ||
(positive_int_p(x) &&
negative_int_p(y)))) {
return rb_funcall(z, '-', 1, y);
}
return z;
} x.remainder(y) means x-y*(x/y).truncate
См. #divmod.
static VALUE
num_round(int argc, VALUE* argv, VALUE num)
{
return flo_round(argc, argv, rb_Float(num));
} Округляет num до заданной точности в десятичных знаках (по умолчанию 0 знаков).
Точность может быть отрицательной. Возвращает число с плавающей точкой, когда ndigits больше нуля.
Numeric реализует это, преобразуя себя в Float и вызывая Float#round.
static VALUE
num_sadded(VALUE x, VALUE name)
{
ID mid = rb_to_id(name);
/* ruby_frame = ruby_frame->prev; */ /* pop frame for "singleton_method_added" */
rb_remove_method_id(rb_singleton_class(x), mid);
rb_raise(rb_eTypeError,
"can't define singleton method \"%"PRIsVALUE"\" for %"PRIsVALUE,
rb_id2str(mid),
rb_obj_class(x));
UNREACHABLE;
} Перехватывает попытки добавить методы к объектам Numeric. Всегда вызывает TypeError.
Числа должны быть значениями; к ним не следует добавлять singleton-методы.
static VALUE
num_step(int argc, VALUE *argv, VALUE from)
{
VALUE to, step;
int desc, inf;
RETURN_SIZED_ENUMERATOR(from, argc, argv, num_step_size);
desc = num_step_scan_args(argc, argv, &to, &step);
if (RTEST(rb_num_coerce_cmp(step, INT2FIX(0), id_eq))) {
inf = 1;
}
else if (RB_TYPE_P(to, T_FLOAT)) {
double f = RFLOAT_VALUE(to);
inf = isinf(f) && (signbit(f) ? desc : !desc);
}
else inf = 0;
if (FIXNUM_P(from) && (inf || FIXNUM_P(to)) && FIXNUM_P(step)) {
long i = FIX2LONG(from);
long diff = FIX2LONG(step);
if (inf) {
for (;; i += diff)
rb_yield(LONG2FIX(i));
}
else {
long end = FIX2LONG(to);
if (desc) {
for (; i >= end; i += diff)
rb_yield(LONG2FIX(i));
}
else {
for (; i <= end; i += diff)
rb_yield(LONG2FIX(i));
}
}
}
else if (!ruby_float_step(from, to, step, FALSE)) {
VALUE i = from;
if (inf) {
for (;; i = rb_funcall(i, '+', 1, step))
rb_yield(i);
}
else {
ID cmp = desc ? '<' : '>';
for (; !RTEST(rb_funcall(i, cmp, 1, to)); i = rb_funcall(i, '+', 1, step))
rb_yield(i);
}
}
return from;
} Вызывает предоставленный блок с последовательностью чисел, начиная с num, увеличивая на step (по умолчанию 1) на каждом вызове.
Цикл завершается, когда значение, которое будет передано блоку, становится больше limit (если step положительно) или меньше limit (если step отрицательно), где limit по умолчанию равен бесконечности.
В рекомендуемом стиле ключевых аргументов можно опустить один или оба step и limit (по умолчанию бесконечность). В стиле аргументов фиксированной позиции не разрешено использовать ноль в качестве шага (т.е. num.step(limit, 0)) по соображениям обратной совместимости.
Если все аргументы являются целыми числами, цикл работает с целочисленным счётчиком.
Если какой-либо из аргументов является числом с плавающей точкой, все они преобразуются в числа с плавающей точкой, и цикл выполняется следующим выражением:
floor(n + n*epsilon)+ 1
Где n — следующее:
n = (limit - num)/step
В противном случае цикл начинается с num, использует оператор меньше (<) или больше (>) для сравнения счётчика с limit, и увеличивает его с помощью оператора +.
Если блок не задан, вместо этого возвращается Enumerator.
Например:
p 1.step.take(4)
p 10.step(by: -1).take(4)
3.step(to: 5) { |i| print i, " " }
1.step(10, 2) { |i| print i, " " }
Math::E.step(to: Math::PI, by: 0.2) { |f| print f, " " }
Что даст:
[1, 2, 3, 4] [10, 9, 8, 7] 3 4 5 1 3 5 7 9 2.71828182845905 2.91828182845905 3.11828182845905
static VALUE
numeric_to_c(VALUE self)
{
return rb_complex_new1(self);
} Возвращает значение в виде комплексного числа.
static VALUE
num_to_int(VALUE num)
{
return rb_funcall(num, id_to_i, 0, 0);
} Вызывает метод to_i дочернего класса для преобразования num в целое число.
1.0.class => Float 1.0.to_int.class => Fixnum 1.0.to_i.class => Fixnum
static VALUE
num_truncate(VALUE num)
{
return flo_truncate(rb_Float(num));
} Возвращает num, усечённое до целого числа.
Numeric реализует это, преобразуя своё значение в Float и вызывая Float#truncate.
static VALUE
num_zero_p(VALUE num)
{
if (rb_equal(num, INT2FIX(0))) {
return Qtrue;
}
return Qfalse;
} Возвращает true, если num имеет нулевое значение.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.