ruby-changes:64043
From: Kenta <ko1@a...>
Date: Wed, 9 Dec 2020 18:49:15 +0900 (JST)
Subject: [ruby-changes:64043] fad3023e94 (master): Fix ArithmeticSequence#last and ArithmeticSequence#each for non-integer sequences (#3870)
https://git.ruby-lang.org/ruby.git/commit/?id=fad3023e94 From fad3023e94c45e7f03478732f7641b6f39ba9d12 Mon Sep 17 00:00:00 2001 From: Kenta Murata <mrkn@u...> Date: Wed, 9 Dec 2020 18:48:59 +0900 Subject: Fix ArithmeticSequence#last and ArithmeticSequence#each for non-integer sequences (#3870) [Bug #17218] [ruby-core:100312] diff --git a/common.mk b/common.mk index c2d6a72..c7d331f 100644 --- a/common.mk +++ b/common.mk @@ -4823,6 +4823,7 @@ enumerator.$(OBJEXT): $(top_srcdir)/internal/hash.h https://github.com/ruby/ruby/blob/trunk/common.mk#L4823 enumerator.$(OBJEXT): $(top_srcdir)/internal/imemo.h enumerator.$(OBJEXT): $(top_srcdir)/internal/numeric.h enumerator.$(OBJEXT): $(top_srcdir)/internal/range.h +enumerator.$(OBJEXT): $(top_srcdir)/internal/rational.h enumerator.$(OBJEXT): $(top_srcdir)/internal/serial.h enumerator.$(OBJEXT): $(top_srcdir)/internal/static_assert.h enumerator.$(OBJEXT): $(top_srcdir)/internal/string.h @@ -4989,6 +4990,7 @@ enumerator.$(OBJEXT): {$(VPATH)}internal/xmalloc.h https://github.com/ruby/ruby/blob/trunk/common.mk#L4990 enumerator.$(OBJEXT): {$(VPATH)}missing.h enumerator.$(OBJEXT): {$(VPATH)}onigmo.h enumerator.$(OBJEXT): {$(VPATH)}oniguruma.h +enumerator.$(OBJEXT): {$(VPATH)}ruby_assert.h enumerator.$(OBJEXT): {$(VPATH)}st.h enumerator.$(OBJEXT): {$(VPATH)}subst.h error.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h diff --git a/enumerator.c b/enumerator.c index 953d8f1..68cfc2b 100644 --- a/enumerator.c +++ b/enumerator.c @@ -26,6 +26,7 @@ https://github.com/ruby/ruby/blob/trunk/enumerator.c#L26 #include "internal/imemo.h" #include "internal/numeric.h" #include "internal/range.h" +#include "internal/rational.h" #include "ruby/ruby.h" /* @@ -3601,6 +3602,88 @@ arith_seq_first(int argc, VALUE *argv, VALUE self) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L3602 return rb_call_super(argc, argv); } +static inline VALUE +num_plus(VALUE a, VALUE b) +{ + if (RB_INTEGER_TYPE_P(a)) { + return rb_int_plus(a, b); + } + else if (RB_FLOAT_TYPE_P(a)) { + return rb_float_plus(a, b); + } + else if (RB_TYPE_P(a, T_RATIONAL)) { + return rb_rational_plus(a, b); + } + else { + return rb_funcallv(a, '+', 1, &b); + } +} + +static inline VALUE +num_minus(VALUE a, VALUE b) +{ + if (RB_INTEGER_TYPE_P(a)) { + return rb_int_minus(a, b); + } + else if (RB_FLOAT_TYPE_P(a)) { + return rb_float_minus(a, b); + } + else if (RB_TYPE_P(a, T_RATIONAL)) { + return rb_rational_minus(a, b); + } + else { + return rb_funcallv(a, '-', 1, &b); + } +} + +static inline VALUE +num_mul(VALUE a, VALUE b) +{ + if (RB_INTEGER_TYPE_P(a)) { + return rb_int_mul(a, b); + } + else if (RB_FLOAT_TYPE_P(a)) { + return rb_float_mul(a, b); + } + else if (RB_TYPE_P(a, T_RATIONAL)) { + return rb_rational_mul(a, b); + } + else { + return rb_funcallv(a, '*', 1, &b); + } +} + +static inline VALUE +num_idiv(VALUE a, VALUE b) +{ + VALUE q; + if (RB_INTEGER_TYPE_P(a)) { + q = rb_int_idiv(a, b); + } + else if (RB_FLOAT_TYPE_P(a)) { + q = rb_float_div(a, b); + } + else if (RB_TYPE_P(a, T_RATIONAL)) { + q = rb_rational_div(a, b); + } + else { + q = rb_funcallv(a, idDiv, 1, &b); + } + + if (RB_INTEGER_TYPE_P(q)) { + return q; + } + else if (RB_FLOAT_TYPE_P(q)) { + return rb_float_floor(q, 0); + } + else if (RB_TYPE_P(q, T_RATIONAL)) { + return rb_rational_floor(q, 0); + } + else { + return rb_funcall(q, rb_intern("floor"), 0); + } +} + /* * call-seq: * aseq.last -> num or nil @@ -3625,7 +3708,7 @@ arith_seq_last(int argc, VALUE *argv, VALUE self) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L3708 b = arith_seq_begin(self); s = arith_seq_step(self); - len_1 = rb_int_idiv(rb_int_minus(e, b), s); + len_1 = num_idiv(num_minus(e, b), s); if (rb_num_negative_int_p(len_1)) { if (argc == 0) { return Qnil; @@ -3633,9 +3716,9 @@ arith_seq_last(int argc, VALUE *argv, VALUE self) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L3716 return rb_ary_new_capa(0); } - last = rb_int_plus(b, rb_int_mul(s, len_1)); + last = num_plus(b, num_mul(s, len_1)); if ((last_is_adjusted = arith_seq_exclude_end_p(self) && rb_equal(last, e))) { - last = rb_int_minus(last, s); + last = num_minus(last, s); } if (argc == 0) { @@ -3844,22 +3927,22 @@ arith_seq_each(VALUE self) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L3927 return self; } - len_1 = rb_int_idiv(rb_int_minus(e, c), s); - last = rb_int_plus(c, rb_int_mul(s, len_1)); + len_1 = num_idiv(num_minus(e, c), s); + last = num_plus(c, num_mul(s, len_1)); if (x && rb_equal(last, e)) { - last = rb_int_minus(last, s); + last = num_minus(last, s); } if (rb_num_negative_int_p(s)) { while (NUM_GE(c, last)) { rb_yield(c); - c = rb_int_plus(c, s); + c = num_plus(c, s); } } else { while (NUM_GE(last, c)) { rb_yield(c); - c = rb_int_plus(c, s); + c = num_plus(c, s); } } diff --git a/internal/numeric.h b/internal/numeric.h index fa24927..3dc72d2 100644 --- a/internal/numeric.h +++ b/internal/numeric.h @@ -55,6 +55,7 @@ VALUE rb_float_uminus(VALUE num); https://github.com/ruby/ruby/blob/trunk/internal/numeric.h#L55 VALUE rb_int_plus(VALUE x, VALUE y); VALUE rb_float_plus(VALUE x, VALUE y); VALUE rb_int_minus(VALUE x, VALUE y); +VALUE rb_float_minus(VALUE x, VALUE y); VALUE rb_int_mul(VALUE x, VALUE y); VALUE rb_float_mul(VALUE x, VALUE y); VALUE rb_float_div(VALUE x, VALUE y); @@ -79,6 +80,7 @@ int rb_int_positive_p(VALUE num); https://github.com/ruby/ruby/blob/trunk/internal/numeric.h#L80 int rb_int_negative_p(VALUE num); VALUE rb_num_pow(VALUE x, VALUE y); VALUE rb_float_ceil(VALUE num, int ndigits); +VALUE rb_float_floor(VALUE x, int ndigits); VALUE rb_float_abs(VALUE flt); static inline VALUE rb_num_compare_with_zero(VALUE num, ID mid); static inline int rb_num_positive_int_p(VALUE num); diff --git a/internal/rational.h b/internal/rational.h index 18efd7b..e53ee7b 100644 --- a/internal/rational.h +++ b/internal/rational.h @@ -29,12 +29,14 @@ VALUE rb_rational_uminus(VALUE self); https://github.com/ruby/ruby/blob/trunk/internal/rational.h#L29 VALUE rb_rational_plus(VALUE self, VALUE other); VALUE rb_rational_minus(VALUE self, VALUE other); VALUE rb_rational_mul(VALUE self, VALUE other); +VALUE rb_rational_div(VALUE self, VALUE other); VALUE rb_lcm(VALUE x, VALUE y); VALUE rb_rational_reciprocal(VALUE x); VALUE rb_cstr_to_rat(const char *, int); VALUE rb_rational_abs(VALUE self); VALUE rb_rational_cmp(VALUE self, VALUE other); VALUE rb_rational_pow(VALUE self, VALUE other); +VALUE rb_rational_floor(VALUE self, int ndigits); VALUE rb_numeric_quo(VALUE x, VALUE y); VALUE rb_float_numerator(VALUE x); VALUE rb_float_denominator(VALUE x); diff --git a/numeric.c b/numeric.c index fe0a186..ab6e6d9 100644 --- a/numeric.c +++ b/numeric.c @@ -1078,8 +1078,8 @@ rb_float_plus(VALUE x, VALUE y) https://github.com/ruby/ruby/blob/trunk/numeric.c#L1078 * Returns a new Float which is the difference of +float+ and +other+. */ -static VALUE -flo_minus(VALUE x, VALUE y) +VALUE +rb_float_minus(VALUE x, VALUE y) { if (RB_TYPE_P(y, T_FIXNUM)) { return DBL2NUM(RFLOAT_VALUE(x) - (double)FIX2LONG(y)); @@ -1935,6 +1935,31 @@ flo_prev_float(VALUE vx) https://github.com/ruby/ruby/blob/trunk/numeric.c#L1935 return flo_nextafter(vx, -HUGE_VAL); } +VALUE +rb_float_floor(VALUE num, int ndigits) +{ + double number, f; + number = RFLOAT_VALUE(num); + if (number == 0.0) { + return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0); + } + if (ndigits > 0) { + int binexp; + frexp(number, &binexp); + if (float_round_overflow(ndigits, binexp)) return num; + if (number > 0.0 && float_round_underflow(ndigits, binexp)) + return DBL2NUM(0.0); + f = pow(10, ndigits); + f = floor(number * f) / f; + return DBL2NUM(f); + } + else { + num = dbl2ival(floor(number)); + if (ndigits < 0) num = rb_int_floor(num, ndigits); + return num; + } +} + /* * call-seq: * float.floor([ndigits]) -> integer or float @@ -1977,31 +2002,11 @@ flo_prev_float(VALUE vx) https://github.com/ruby/ruby/blob/trunk/numeric.c#L2002 static VALUE flo_floor(int argc, VALUE *argv, VALUE num) { - double number, f; int ndigits = 0; - if (rb_check_arity(argc, 0, 1)) { ndigits = NUM2INT(argv[0]); } - number = RFLOAT_VALUE(num); - if (number == 0.0) { - return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0); - } - if (ndigits > 0) { - int binexp; - frexp(number, &binexp); - if (float_round_overflow(ndigits, binexp)) return num; - if (number > 0.0 && float_round_underflow(ndigits, binexp)) - return DBL2NUM(0.0); - f = pow(10, ndigits); - f = floor(number * f) / f; - return DBL2NUM(f); - } - else { - num = dbl2ival(floor(number)); - if (ndigits < 0) num = rb_int_floor(num, ndigits); - return num; - } + return rb_float_floor(num, ndigits); } /* @@ -5749,7 +5754,7 @@ Init_Numeric(void) https://github.com/ruby/ruby/blob/trunk/numeric.c#L5754 rb_define_method(rb_cFloat, "coerce", flo_coerce, 1); rb_define_method(rb_cFloat, "-@", rb_float_uminus, 0); rb_define_method(rb_cFloat, "+", rb_float_plus, 1); - rb_define_method(rb_cFloat, "-", flo_minus, 1); + rb_define_method(rb_cFloat, "-", rb_float_minus, 1); rb_define_method(rb_cFloat, "*", rb_float_mul, 1); rb_define_method(rb_cFloat, "/", rb_float_div, 1); rb_define_method(rb_cFloat, "quo", flo_quo, 1); diff --git a/rational.c b/rational.c index bb7c66c..ae134da 100644 --- a/rational.c +++ b/rational.c @@ -894,8 +894,8 @@ rb_rational_mul(VALUE self, VA (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/