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
127
enumerator.c
127
enumerator.c
@ -2809,27 +2809,132 @@ rb_arithmetic_sequence_extract(VALUE obj, rb_arithmetic_sequence_components_t *c
|
|||||||
static VALUE
|
static VALUE
|
||||||
arith_seq_first(int argc, VALUE *argv, VALUE self)
|
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);
|
b = arith_seq_begin(self);
|
||||||
e = arith_seq_end(self);
|
e = arith_seq_end(self);
|
||||||
s = arith_seq_step(self);
|
s = arith_seq_step(self);
|
||||||
|
if (argc == 0) {
|
||||||
if (!NIL_P(e)) {
|
if (!NIL_P(e)) {
|
||||||
len_1 = rb_int_idiv(rb_int_minus(e, b), s);
|
VALUE zero = INT2FIX(0);
|
||||||
if (rb_num_negative_int_p(len_1)) {
|
int r = rb_cmpint(rb_num_coerce_cmp(s, zero, idCmp), s, zero);
|
||||||
if (argc == 0) {
|
if (r > 0 && RTEST(rb_funcall(b, '>', 1, e))) {
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
if (r < 0 && RTEST(rb_funcall(b, '<', 1, e))) {
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
return rb_ary_new_capa(0);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (argc == 0) {
|
|
||||||
return b;
|
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);
|
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);
|
int rb_num_to_uint(VALUE val, unsigned int *ret);
|
||||||
VALUE ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl);
|
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);
|
int ruby_float_step(VALUE from, VALUE to, VALUE step, int excl, int allow_endless);
|
||||||
double ruby_float_mod(double x, double y);
|
double ruby_float_mod(double x, double y);
|
||||||
int rb_num_negative_p(VALUE);
|
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));
|
return flo_truncate(argc, argv, rb_Float(num));
|
||||||
}
|
}
|
||||||
|
|
||||||
static double
|
double
|
||||||
ruby_float_step_size(double beg, double end, double unit, int excl)
|
ruby_float_step_size(double beg, double end, double unit, int excl)
|
||||||
{
|
{
|
||||||
const double epsilon = DBL_EPSILON;
|
const double epsilon = DBL_EPSILON;
|
||||||
|
@ -141,12 +141,31 @@ class TestArithmeticSequence < Test::Unit::TestCase
|
|||||||
assert_equal([], seq.first(1))
|
assert_equal([], seq.first(1))
|
||||||
assert_equal([], seq.first(3))
|
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)
|
seq = 10.0.step(-1.0, by: -2.0)
|
||||||
assert_equal(10.0, seq.first)
|
assert_equal(10.0, seq.first)
|
||||||
assert_equal([10.0], seq.first(1))
|
assert_equal([10.0], seq.first(1))
|
||||||
assert_equal([10.0, 8.0, 6.0], seq.first(3))
|
assert_equal([10.0, 8.0, 6.0], seq.first(3))
|
||||||
end
|
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
|
def test_last
|
||||||
seq = 1.step(10)
|
seq = 1.step(10)
|
||||||
assert_equal(10, seq.last)
|
assert_equal(10, seq.last)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user