mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
enumerator.c: fix arith_seq_first for Infinity
* enumerator.c (arith_seq_first): fix for Float::INFINITY. * test/ruby/test_arithmetic_sequence.rb: add tests. * numeric.c (ruby_float_step_size): export for internal use. * internal.h: add prototype declaration of ruby_float_step_size. [ruby-core:90937][Bug #15518] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66947 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
e1e3d642bf
commit
6f6cf042d2
4 changed files with 137 additions and 12 deletions
127
enumerator.c
127
enumerator.c
|
@ -2809,27 +2809,132 @@ rb_arithmetic_sequence_extract(VALUE obj, rb_arithmetic_sequence_components_t *c
|
|||
static VALUE
|
||||
arith_seq_first(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE b, e, s, len_1;
|
||||
VALUE b, e, s, ary;
|
||||
long n;
|
||||
int x;
|
||||
|
||||
rb_check_arity(argc, 0, 1);
|
||||
|
||||
b = arith_seq_begin(self);
|
||||
e = arith_seq_end(self);
|
||||
s = arith_seq_step(self);
|
||||
|
||||
if (!NIL_P(e)) {
|
||||
len_1 = rb_int_idiv(rb_int_minus(e, b), s);
|
||||
if (rb_num_negative_int_p(len_1)) {
|
||||
if (argc == 0) {
|
||||
if (argc == 0) {
|
||||
if (!NIL_P(e)) {
|
||||
VALUE zero = INT2FIX(0);
|
||||
int r = rb_cmpint(rb_num_coerce_cmp(s, zero, idCmp), s, zero);
|
||||
if (r > 0 && RTEST(rb_funcall(b, '>', 1, e))) {
|
||||
return Qnil;
|
||||
}
|
||||
if (r < 0 && RTEST(rb_funcall(b, '<', 1, e))) {
|
||||
return Qnil;
|
||||
}
|
||||
return rb_ary_new_capa(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc == 0) {
|
||||
return b;
|
||||
}
|
||||
|
||||
/* TODO: optimization */
|
||||
// TODO: the following code should be extracted as arith_seq_take
|
||||
|
||||
n = NUM2LONG(argv[0]);
|
||||
if (n < 0) {
|
||||
rb_raise(rb_eArgError, "attempt to take negative size");
|
||||
}
|
||||
if (n == 0) {
|
||||
return rb_ary_new_capa(0);
|
||||
}
|
||||
|
||||
x = arith_seq_exclude_end_p(self);
|
||||
|
||||
if (FIXNUM_P(b) && NIL_P(e) && FIXNUM_P(s)) {
|
||||
long i = FIX2LONG(b), unit = FIX2LONG(s);
|
||||
ary = rb_ary_new_capa(n);
|
||||
while (n > 0 && FIXABLE(i)) {
|
||||
rb_ary_push(ary, LONG2FIX(i));
|
||||
i += unit; // FIXABLE + FIXABLE never overflow;
|
||||
--n;
|
||||
}
|
||||
if (n > 0) {
|
||||
b = LONG2NUM(i);
|
||||
while (n > 0) {
|
||||
rb_ary_push(ary, b);
|
||||
b = rb_big_plus(b, s);
|
||||
--n;
|
||||
}
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
else if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(s)) {
|
||||
long i = FIX2LONG(b);
|
||||
long end = FIX2LONG(e);
|
||||
long unit = FIX2LONG(s);
|
||||
long len;
|
||||
|
||||
if (unit >= 0) {
|
||||
if (!x) end += 1;
|
||||
|
||||
len = end - i;
|
||||
if (len < 0) len = 0;
|
||||
ary = rb_ary_new_capa((n < len) ? n : len);
|
||||
while (n > 0 && i < end) {
|
||||
rb_ary_push(ary, LONG2FIX(i));
|
||||
if (i + unit < i) break;
|
||||
i += unit;
|
||||
--n;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!x) end -= 1;
|
||||
|
||||
len = i - end;
|
||||
if (len < 0) len = 0;
|
||||
ary = rb_ary_new_capa((n < len) ? n : len);
|
||||
while (n > 0 && i > end) {
|
||||
rb_ary_push(ary, LONG2FIX(i));
|
||||
if (i + unit > i) break;
|
||||
i += unit;
|
||||
--n;
|
||||
}
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
else if (RB_FLOAT_TYPE_P(b) || RB_FLOAT_TYPE_P(e) || RB_FLOAT_TYPE_P(s)) {
|
||||
/* generate values like ruby_float_step */
|
||||
|
||||
double unit = NUM2DBL(s);
|
||||
double beg = NUM2DBL(b);
|
||||
double end = NIL_P(e) ? (unit < 0 ? -1 : 1)*HUGE_VAL : NUM2DBL(e);
|
||||
double len = ruby_float_step_size(beg, end, unit, x);
|
||||
long i;
|
||||
|
||||
if (n > len)
|
||||
n = (long)len;
|
||||
|
||||
if (isinf(unit)) {
|
||||
if (len > 0) {
|
||||
ary = rb_ary_new_capa(1);
|
||||
rb_ary_push(ary, DBL2NUM(beg));
|
||||
}
|
||||
else {
|
||||
ary = rb_ary_new_capa(0);
|
||||
}
|
||||
}
|
||||
else if (unit == 0) {
|
||||
VALUE val = DBL2NUM(beg);
|
||||
ary = rb_ary_new_capa(n);
|
||||
for (i = 0; i < len; ++i) {
|
||||
rb_ary_push(ary, val);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ary = rb_ary_new_capa(n);
|
||||
for (i = 0; i < n; ++i) {
|
||||
double d = i*unit+beg;
|
||||
if (unit >= 0 ? end < d : d < end) d = end;
|
||||
rb_ary_push(ary, DBL2NUM(d));
|
||||
}
|
||||
}
|
||||
|
||||
return ary;
|
||||
}
|
||||
|
||||
return rb_call_super(argc, argv);
|
||||
}
|
||||
|
|
|
@ -1639,6 +1639,7 @@ enum ruby_num_rounding_mode {
|
|||
|
||||
int rb_num_to_uint(VALUE val, unsigned int *ret);
|
||||
VALUE ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl);
|
||||
double ruby_float_step_size(double beg, double end, double unit, int excl);
|
||||
int ruby_float_step(VALUE from, VALUE to, VALUE step, int excl, int allow_endless);
|
||||
double ruby_float_mod(double x, double y);
|
||||
int rb_num_negative_p(VALUE);
|
||||
|
|
|
@ -2481,7 +2481,7 @@ num_truncate(int argc, VALUE *argv, VALUE num)
|
|||
return flo_truncate(argc, argv, rb_Float(num));
|
||||
}
|
||||
|
||||
static double
|
||||
double
|
||||
ruby_float_step_size(double beg, double end, double unit, int excl)
|
||||
{
|
||||
const double epsilon = DBL_EPSILON;
|
||||
|
|
|
@ -141,12 +141,31 @@ class TestArithmeticSequence < Test::Unit::TestCase
|
|||
assert_equal([], seq.first(1))
|
||||
assert_equal([], seq.first(3))
|
||||
|
||||
seq = 1.step(10, by: 0)
|
||||
assert_equal(1, seq.first)
|
||||
assert_equal([1], seq.first(1))
|
||||
assert_equal([1, 1, 1], seq.first(3))
|
||||
|
||||
seq = 10.0.step(-1.0, by: -2.0)
|
||||
assert_equal(10.0, seq.first)
|
||||
assert_equal([10.0], seq.first(1))
|
||||
assert_equal([10.0, 8.0, 6.0], seq.first(3))
|
||||
end
|
||||
|
||||
def test_first_bug15518
|
||||
bug15518 = '[Bug #15518]'
|
||||
seq = (1 .. 10.0).step(1)
|
||||
five_float_classes = Array.new(5) { Float }
|
||||
assert_equal(five_float_classes, seq.first(5).map(&:class), bug15518)
|
||||
assert_equal([1.0, 2.0, 3.0, 4.0, 5.0], seq.first(5), bug15518)
|
||||
seq = (1 .. Float::INFINITY).step(1)
|
||||
assert_equal(five_float_classes, seq.first(5).map(&:class), bug15518)
|
||||
assert_equal([1.0, 2.0, 3.0, 4.0, 5.0], seq.first(5), bug15518)
|
||||
seq = (1 .. Float::INFINITY).step(1r)
|
||||
assert_equal(five_float_classes, seq.first(5).map(&:class), bug15518)
|
||||
assert_equal([1.0, 2.0, 3.0, 4.0, 5.0], seq.first(5), bug15518)
|
||||
end
|
||||
|
||||
def test_last
|
||||
seq = 1.step(10)
|
||||
assert_equal(10, seq.last)
|
||||
|
|
Loading…
Reference in a new issue