类 Numeric

Numeric 是所有高级数字类应该继承的类。

Numeric 允许实例化堆分配的对象。其他核心数字类,如 Integer,被实现为立即数,这意味着每个 Integer 都是一个单一的不可变对象,总是按值传递。

a = 1
1.object_id == a.object_id   #=> true

例如,只能存在一个整数 1 的实例。Ruby 通过阻止实例化来确保这一点。如果尝试复制,将返回相同的实例。

Integer.new(1)                   #=> NoMethodError: undefined method `new' for Integer:Class
1.dup                            #=> 1
1.object_id == 1.dup.object_id   #=> true

因此,在定义其他数字类时应使用 Numeric。

从 Numeric 继承的类必须实现 coerce,它返回一个包含两个元素的 Array,其中包含已强制转换为新类实例的对象和 self(参见 coerce)。

继承的类还应实现算术运算符方法(+-*/)以及 <=> 运算符(参见 Comparable)。这些方法可能依赖于 coerce 来确保与其他数字类实例的互操作性。

class Tally < Numeric
  def initialize(string)
    @string = string
  end

  def to_s
    @string
  end

  def to_i
    @string.size
  end

  def coerce(other)
    [self.class.new('|' * other.to_i), self]
  end

  def <=>(other)
    to_i <=> other.to_i
  end

  def +(other)
    self.class.new('|' * (to_i + other.to_i))
  end

  def -(other)
    self.class.new('|' * (to_i - other.to_i))
  end

  def *(other)
    self.class.new('|' * (to_i * other.to_i))
  end

  def /(other)
    self.class.new('|' * (to_i / other.to_i))
  end
end

tally = Tally.new('||')
puts tally * 2            #=> "||||"
puts tally > 1            #=> true

内容

首先,其他地方的内容。类 Numeric

这里,类 Numeric 提供了以下方法

查询

比较

转换

其他

公共实例方法

self % other → real_numeric click to toggle source

返回selfother的实数结果。

在核心和标准库类中,只有Rational使用此实现。

对于Rational r和实数n,这些表达式是等价的

r % n
r-n*(r/n).floor
r.divmod(n)[1]

参见 Numeric#divmod

示例

r = Rational(1, 2)    # => (1/2)
r2 = Rational(2, 3)   # => (2/3)
r % r2                # => (1/2)
r % 2                 # => (1/2)
r % 2.0               # => 0.5

r = Rational(301,100) # => (301/100)
r2 = Rational(7,5)    # => (7/5)
r % r2                # => (21/100)
r % -r2               # => (-119/100)
(-r) % r2             # => (119/100)
(-r) %-r2             # => (-21/100)
static VALUE
num_modulo(VALUE x, VALUE y)
{
    VALUE q = num_funcall1(x, id_div, y);
    return rb_funcall(x, '-', 1,
                      rb_funcall(y, '*', 1, q));
}
别名:modulo
+self → self 点击切换源代码

返回 self

static VALUE
num_uplus(VALUE num)
{
    return num;
}
-self → numeric 点击切换源代码

一元负号 - 返回接收者的负值。

static VALUE
num_uminus(VALUE num)
{
    VALUE zero;

    zero = INT2FIX(0);
    do_coerce(&zero, &num, TRUE);

    return num_funcall1(zero, '-', num);
}
self <=> other → zero or nil 点击切换源代码

如果 selfother 相同,则返回零,否则返回 nil

Ruby 核心或标准库中的任何子类都不使用此实现。

static VALUE
num_cmp(VALUE x, VALUE y)
{
    if (x == y) return INT2FIX(0);
    return Qnil;
}
abs → numeric 点击切换源代码

返回 self 的绝对值。

12.abs        #=> 12
(-34.56).abs  #=> 34.56
-34.56.abs    #=> 34.56
static VALUE
num_abs(VALUE num)
{
    if (rb_num_negative_int_p(num)) {
        return num_funcall0(num, idUMinus);
    }
    return num;
}
别名:magnitude
abs2 → real 点击切换源代码

返回 self 的平方。

static VALUE
numeric_abs2(VALUE self)
{
    return f_mul(self, self);
}
angle
别名:arg
arg → 0 or Math::PI 点击切换源代码

如果 self 为正,则返回零,否则返回 Math::PI。

static VALUE
numeric_arg(VALUE self)
{
    if (f_positive_p(self))
        return INT2FIX(0);
    return DBL2NUM(M_PI);
}
别名:angle, phase
ceil(digits = 0) → integer or float 点击切换源代码

返回大于或等于 self 的最小数字,精度为 digits 个小数位。

Numeric 通过将 self 转换为 Float 并调用 Float#ceil 来实现此功能。

static VALUE
num_ceil(int argc, VALUE *argv, VALUE num)
{
    return flo_ceil(argc, argv, rb_Float(num));
}
clone(freeze: true) → self 点击切换源代码

返回 self

如果 freeze 的值为 truenil 之外,则会引发异常。

相关:Numeric#dup

static VALUE
num_clone(int argc, VALUE *argv, VALUE x)
{
    return rb_immutable_obj_clone(argc, argv, x);
}
coerce(other) → array 点击切换源代码

返回一个包含两个数字元素的 2 元素数组,该数组由两个操作数 selfother 组成,它们具有共同的兼容类型。

在核心和标准库类中,IntegerRationalComplex 使用此实现。

示例

i = 2                    # => 2
i.coerce(3)              # => [3, 2]
i.coerce(3.0)            # => [3.0, 2.0]
i.coerce(Rational(1, 2)) # => [0.5, 2.0]
i.coerce(Complex(3, 4))  # Raises RangeError.

r = Rational(5, 2)       # => (5/2)
r.coerce(2)              # => [(2/1), (5/2)]
r.coerce(2.0)            # => [2.0, 2.5]
r.coerce(Rational(2, 3)) # => [(2/3), (5/2)]
r.coerce(Complex(3, 4))  # => [(3+4i), ((5/2)+0i)]

c = Complex(2, 3)        # => (2+3i)
c.coerce(2)              # => [(2+0i), (2+3i)]
c.coerce(2.0)            # => [(2.0+0i), (2+3i)]
c.coerce(Rational(1, 2)) # => [((1/2)+0i), (2+3i)]
c.coerce(Complex(3, 4))  # => [(3+4i), (2+3i)]

如果任何类型转换失败,则引发异常。

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);
}
conj → self
别名:conjugate
conjugate
也称为别名:conj
denominator → integer 点击切换源代码

返回分母(始终为正数)。

static VALUE
numeric_denominator(VALUE self)
{
    return f_denominator(f_to_r(self));
}
div(other) → integer 点击切换源代码

返回商 self/other 作为整数(通过 floor),使用 self 派生类中的方法 /。(Numeric 本身没有定义方法 /。)

在核心和标准库类中,只有 FloatRational 使用此实现。

static VALUE
num_div(VALUE x, VALUE y)
{
    if (rb_equal(INT2FIX(0), y)) rb_num_zerodiv();
    return rb_funcall(num_funcall1(x, '/', y), rb_intern("floor"), 0);
}
divmod(other) → array 点击切换源代码

返回一个包含 2 个元素的数组 [q, r],其中

q = (self/other).floor                  # Quotient
r = self % other                        # Remainder

在核心和标准库类中,只有Rational使用此实现。

示例

Rational(11, 1).divmod(4)               # => [2, (3/1)]
Rational(11, 1).divmod(-4)              # => [-3, (-1/1)]
Rational(-11, 1).divmod(4)              # => [-3, (1/1)]
Rational(-11, 1).divmod(-4)             # => [2, (-3/1)]

Rational(12, 1).divmod(4)               # => [3, (0/1)]
Rational(12, 1).divmod(-4)              # => [-3, (0/1)]
Rational(-12, 1).divmod(4)              # => [-3, (0/1)]
Rational(-12, 1).divmod(-4)             # => [3, (0/1)]

Rational(13, 1).divmod(4.0)             # => [3, 1.0]
Rational(13, 1).divmod(Rational(4, 11)) # => [35, (3/11)]
static VALUE
num_divmod(VALUE x, VALUE y)
{
    return rb_assoc_new(num_div(x, y), num_modulo(x, y));
}
dup → self 点击切换源代码

返回 self

相关:Numeric#clone.

static VALUE
num_dup(VALUE x)
{
    return x;
}
eql?(other) → true or false 点击切换源代码

如果 selfother 是相同类型且具有相同的值,则返回 true

在核心和标准库类中,只有 IntegerRationalComplex 使用此实现。

示例

1.eql?(1)              # => true
1.eql?(1.0)            # => false
1.eql?(Rational(1, 1)) # => false
1.eql?(Complex(1, 0))  # => false

方法 eql? 与 +==+ 不同,因为 eql? 需要匹配类型,而 +==+ 不需要。

static VALUE
num_eql(VALUE x, VALUE y)
{
    if (TYPE(x) != TYPE(y)) return Qfalse;

    if (RB_BIGNUM_TYPE_P(x)) {
        return rb_big_eql(x, y);
    }

    return rb_equal(x, y);
}
fdiv(other) → float 点击切换源代码

返回商 self/other 作为浮点数,使用 self 派生类中的方法 /。(Numeric 本身没有定义方法 /。)

在核心和标准库类中,只有 BigDecimal 使用此实现。

static VALUE
num_fdiv(VALUE x, VALUE y)
{
    return rb_funcall(rb_Float(x), '/', 1, y);
}
finite? → true or false 点击切换源代码

如果 self 是一个有限数,则返回 true,否则返回 false

# File numeric.rb, line 38
def finite?
  true
end
floor(digits = 0) → integer or float 点击切换源代码

返回小于或等于 self 且精度为 digits 个小数位的最大数。

Numeric 通过将 self 转换为 Float 并调用 Float#floor 来实现此功能。

static VALUE
num_floor(int argc, VALUE *argv, VALUE num)
{
    return flo_floor(argc, argv, rb_Float(num));
}
i → complex 点击切换源代码

返回 Complex(0, self)

2.i              # => (0+2i)
-2.i             # => (0-2i)
2.0.i            # => (0+2.0i)
Rational(1, 2).i # => (0+(1/2)*i)
Complex(3, 4).i  # Raises NoMethodError.
static VALUE
num_imaginary(VALUE num)
{
    return rb_complex_new(INT2FIX(0), num);
}
imag → 0
别名:imaginary
imaginary
也称为:imag
infinite? → -1, 1, or nil 点击切换源代码

根据 self 是否为有限值、-Infinity+Infinity 返回 nil、-1 或 1。

# File numeric.rb, line 48
def infinite?
  nil
end
integer? → true or false 点击切换源代码

如果 self 是一个 Integer,则返回 true

1.0.integer? # => false
1.integer?   # => true
# File numeric.rb, line 29
def integer?
  false
end
magnitude
别名:abs
modulo
别名:%
negative? → true or false 点击切换源代码

如果 self 小于 0,则返回 true,否则返回 false

static VALUE
num_negative_p(VALUE num)
{
    return RBOOL(rb_num_negative_int_p(num));
}
nonzero? → self or nil 点击切换源代码

如果 self 不是零值,则返回 self,否则返回 nil;使用方法 zero? 进行评估。

返回的 self 允许方法进行链式调用

a = %w[z Bb bB bb BB a aA Aa AA A]
a.sort {|a, b| (a.downcase <=> b.downcase).nonzero? || a <=> b }
# => ["A", "a", "AA", "Aa", "aA", "BB", "Bb", "bB", "bb", "z"]

在核心和标准库类中,IntegerFloatRationalComplex 使用此实现。

static VALUE
num_nonzero_p(VALUE num)
{
    if (RTEST(num_funcall0(num, rb_intern("zero?")))) {
        return Qnil;
    }
    return num;
}
numerator → integer 点击切换源代码

返回分子。

static VALUE
numeric_numerator(VALUE self)
{
    return f_numerator(f_to_r(self));
}
phase
别名:arg
polar → array 点击切换源代码

返回数组 [self.abs, self.arg]

static VALUE
numeric_polar(VALUE self)
{
    VALUE abs, arg;

    if (RB_INTEGER_TYPE_P(self)) {
        abs = rb_int_abs(self);
        arg = numeric_arg(self);
    }
    else if (RB_FLOAT_TYPE_P(self)) {
        abs = rb_float_abs(self);
        arg = float_arg(self);
    }
    else if (RB_TYPE_P(self, T_RATIONAL)) {
        abs = rb_rational_abs(self);
        arg = numeric_arg(self);
    }
    else {
        abs = f_abs(self);
        arg = f_arg(self);
    }
    return rb_assoc_new(abs, arg);
}
positive? → true or false 点击切换源代码

如果 self 大于 0,则返回 true,否则返回 false

static VALUE
num_positive_p(VALUE num)
{
    const ID mid = '>';

    if (FIXNUM_P(num)) {
        if (method_basic_p(rb_cInteger))
            return RBOOL((SIGNED_VALUE)num > (SIGNED_VALUE)INT2FIX(0));
    }
    else if (RB_BIGNUM_TYPE_P(num)) {
        if (method_basic_p(rb_cInteger))
            return RBOOL(BIGNUM_POSITIVE_P(num) && !rb_bigzero_p(num));
    }
    return rb_num_compare_with_zero(num, mid);
}
quo(int_or_rat) → rat 点击切换源代码
quo(flo) → flo

返回最精确的除法(整数为有理数,浮点数为浮点数)。

VALUE
rb_numeric_quo(VALUE x, VALUE y)
{
    if (RB_TYPE_P(x, T_COMPLEX)) {
        return rb_complex_div(x, y);
    }

    if (RB_FLOAT_TYPE_P(y)) {
        return rb_funcallv(x, idFdiv, 1, &y);
    }

    x = rb_convert_type(x, T_RATIONAL, "Rational", "to_r");
    return rb_rational_div(x, y);
}
real → self 点击切换源代码

返回 self

# File numeric.rb, line 17
def real
  self
end
real? → true or false 点击切换源代码

如果 self 是一个实数(即不是 Complex),则返回 true

# File numeric.rb, line 8
def real?
  true
end
rect → 数组

返回数组 [self, 0]

别名:rectangular
rectangular
也称为:rect
remainder(other) → real_number 点击切换源代码

返回 self 除以 other 后的余数。

在核心和标准库类中,只有 FloatRational 使用此实现。

示例

11.0.remainder(4)              # => 3.0
11.0.remainder(-4)             # => 3.0
-11.0.remainder(4)             # => -3.0
-11.0.remainder(-4)            # => -3.0

12.0.remainder(4)              # => 0.0
12.0.remainder(-4)             # => 0.0
-12.0.remainder(4)             # => -0.0
-12.0.remainder(-4)            # => -0.0

13.0.remainder(4.0)            # => 1.0
13.0.remainder(Rational(4, 1)) # => 1.0

Rational(13, 1).remainder(4)   # => (1/1)
Rational(13, 1).remainder(-4)  # => (1/1)
Rational(-13, 1).remainder(4)  # => (-1/1)
Rational(-13, 1).remainder(-4) # => (-1/1)
static VALUE
num_remainder(VALUE x, VALUE y)
{
    if (!rb_obj_is_kind_of(y, rb_cNumeric)) {
        do_coerce(&x, &y, TRUE);
    }
    VALUE z = num_funcall1(x, '%', y);

    if ((!rb_equal(z, INT2FIX(0))) &&
        ((rb_num_negative_int_p(x) &&
          rb_num_positive_int_p(y)) ||
         (rb_num_positive_int_p(x) &&
          rb_num_negative_int_p(y)))) {
        if (RB_FLOAT_TYPE_P(y)) {
            if (isinf(RFLOAT_VALUE(y))) {
                return x;
            }
        }
        return rb_funcall(z, '-', 1, y);
    }
    return z;
}
round(digits = 0) → integer or float 点击切换源代码

返回 self 四舍五入到最接近的值,精度为 digits 个小数位。

Numeric 通过将 self 转换为 Float 并调用 Float#round 来实现此功能。

static VALUE
num_round(int argc, VALUE* argv, VALUE num)
{
    return flo_round(argc, argv, rb_Float(num));
}
step(to = nil, by = 1) {|n| ... } → self 点击切换源代码
step(to = nil, by = 1) → 枚举器
step(to = nil, by: 1) {|n| ... } → self
step(to = nil, by: 1) → 枚举器
step(by: 1, to: ) {|n| ... } → self
step(by: 1, to: ) → 枚举器
step(by: , to: nil) {|n| ... } → self
step(by: , to: nil) → 枚举器
Generates a sequence of numbers; with a block given, traverses the sequence.

Of the Core and Standard Library classes,
Integer, Float, and Rational use this implementation.

A quick example:

  squares = []
  1.step(by: 2, to: 10) {|i| squares.push(i*i) }
  squares # => [1, 9, 25, 49, 81]

The generated sequence:

- Begins with +self+.
- Continues at intervals of +by+ (which may not be zero).
- Ends with the last number that is within or equal to +to+;
  that is, less than or equal to +to+ if +by+ is positive,
  greater than or equal to +to+ if +by+ is negative.
  If +to+ is +nil+, the sequence is of infinite length.

If a block is given, calls the block with each number in the sequence;
returns +self+.  If no block is given, returns an Enumerator::ArithmeticSequence.

<b>Keyword Arguments</b>

With keyword arguments +by+ and +to+,
their values (or defaults) determine the step and limit:

  # Both keywords given.
  squares = []
  4.step(by: 2, to: 10) {|i| squares.push(i*i) }    # => 4
  squares # => [16, 36, 64, 100]
  cubes = []
  3.step(by: -1.5, to: -3) {|i| cubes.push(i*i*i) } # => 3
  cubes   # => [27.0, 3.375, 0.0, -3.375, -27.0]
  squares = []
  1.2.step(by: 0.2, to: 2.0) {|f| squares.push(f*f) }
  squares # => [1.44, 1.9599999999999997, 2.5600000000000005, 3.24, 4.0]

  squares = []
  Rational(6/5).step(by: 0.2, to: 2.0) {|r| squares.push(r*r) }
  squares # => [1.0, 1.44, 1.9599999999999997, 2.5600000000000005, 3.24, 4.0]

  # Only keyword to given.
  squares = []
  4.step(to: 10) {|i| squares.push(i*i) }           # => 4
  squares # => [16, 25, 36, 49, 64, 81, 100]
  # Only by given.

  # Only keyword by given
  squares = []
  4.step(by:2) {|i| squares.push(i*i); break if i > 10 }
  squares # => [16, 36, 64, 100, 144]

  # No block given.
  e = 3.step(by: -1.5, to: -3) # => (3.step(by: -1.5, to: -3))
  e.class                      # => Enumerator::ArithmeticSequence

<b>Positional Arguments</b>

With optional positional arguments +to+ and +by+,
their values (or defaults) determine the step and limit:

  squares = []
  4.step(10, 2) {|i| squares.push(i*i) }    # => 4
  squares # => [16, 36, 64, 100]
  squares = []
  4.step(10) {|i| squares.push(i*i) }
  squares # => [16, 25, 36, 49, 64, 81, 100]
  squares = []
  4.step {|i| squares.push(i*i); break if i > 10 }  # => nil
  squares # => [16, 25, 36, 49, 64, 81, 100, 121]

实现说明

If all the arguments are integers, the loop operates using an integer
counter.

If any of the arguments are floating point numbers, all are converted
to floats, and the loop is executed
<i>floor(n + n*Float::EPSILON) + 1</i> times,
where <i>n = (limit - self)/step</i>.
static VALUE
num_step(int argc, VALUE *argv, VALUE from)
{
    VALUE to, step;
    int desc, inf;

    if (!rb_block_given_p()) {
        VALUE by = Qundef;

        num_step_extract_args(argc, argv, &to, &step, &by);
        if (!UNDEF_P(by)) {
            step = by;
        }
        if (NIL_P(step)) {
            step = INT2FIX(1);
        }
        else if (rb_equal(step, INT2FIX(0))) {
            rb_raise(rb_eArgError, "step can't be 0");
        }
        if ((NIL_P(to) || rb_obj_is_kind_of(to, rb_cNumeric)) &&
            rb_obj_is_kind_of(step, rb_cNumeric)) {
            return rb_arith_seq_new(from, ID2SYM(rb_frame_this_func()), argc, argv,
                                    num_step_size, from, to, step, FALSE);
        }

        return SIZED_ENUMERATOR_KW(from, 2, ((VALUE [2]){to, step}), num_step_size, FALSE);
    }

    desc = num_step_scan_args(argc, argv, &to, &step, TRUE, FALSE);
    if (rb_equal(step, INT2FIX(0))) {
        inf = 1;
    }
    else if (RB_FLOAT_TYPE_P(to)) {
        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, 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;
}
to_c → complex 点击切换源代码

返回 self 作为 Complex 对象。

static VALUE
numeric_to_c(VALUE self)
{
    return rb_complex_new1(self);
}
to_int → integer 点击切换源代码

返回 self 作为整数;使用派生类中的 to_i 方法进行转换。

在核心和标准库类中,只有 RationalComplex 使用此实现。

示例

Rational(1, 2).to_int # => 0
Rational(2, 1).to_int # => 2
Complex(2, 0).to_int  # => 2
Complex(2, 1)         # Raises RangeError (non-zero imaginary part)
static VALUE
num_to_int(VALUE num)
{
    return num_funcall0(num, id_to_i);
}
truncate(digits = 0) → integer or float 点击切换源代码

返回 self 截断(朝零方向)到 digits 个小数位的精度。

Numeric 通过将 self 转换为 Float 并调用 Float#truncate 来实现此功能。

static VALUE
num_truncate(int argc, VALUE *argv, VALUE num)
{
    return flo_truncate(argc, argv, rb_Float(num));
}
zero? → true or false 点击切换源代码

如果 zero 的值为零,则返回 true,否则返回 false

在核心和标准库类中,只有 RationalComplex 使用此实现。

static VALUE
num_zero_p(VALUE num)
{
    return rb_equal(num, INT2FIX(0));
}