ruby-changes:53402
From: nobu <ko1@a...>
Date: Thu, 8 Nov 2018 11:25:49 +0900 (JST)
Subject: [ruby-changes:53402] nobu:r65618 (trunk): refine parse_rat
nobu 2018-11-08 11:25:44 +0900 (Thu, 08 Nov 2018) New Revision: 65618 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=65618 Log: refine parse_rat * rational.c (read_num): return the exponent instead of the divisor, to get rid of huge bignums. * rational.c (parse_rat): subtract exponents instead of reduction of powers. Modified files: trunk/rational.c trunk/test/ruby/test_rational.rb Index: rational.c =================================================================== --- rational.c (revision 65617) +++ rational.c (revision 65618) @@ -2336,13 +2336,13 @@ negate_num(VALUE num) https://github.com/ruby/ruby/blob/trunk/rational.c#L2336 } static int -read_num(const char **s, const char *const end, VALUE *num, VALUE *div) +read_num(const char **s, const char *const end, VALUE *num, VALUE *nexp) { VALUE fp = ONE, exp, fn = ZERO, n = ZERO; int expsign = 0, ok = 0; char *e; - *div = ONE; + *nexp = ZERO; *num = ZERO; if (*s < end && **s != '.') { n = rb_int_parse_cstr(*s, end-*s, &e, NULL, @@ -2364,10 +2364,9 @@ read_num(const char **s, const char *con https://github.com/ruby/ruby/blob/trunk/rational.c#L2364 return 1; *s = e; { - VALUE l = f_expt10(SIZET2NUM(count)); + VALUE l = f_expt10(*nexp = SIZET2NUM(count)); n = n == ZERO ? fp : rb_int_plus(rb_int_mul(*num, l), fp); *num = n; - *div = l; fn = SIZET2NUM(count); } ok = 1; @@ -2384,18 +2383,12 @@ read_num(const char **s, const char *con https://github.com/ruby/ruby/blob/trunk/rational.c#L2383 if (exp != ZERO) { if (expsign == '-') { if (fn != ZERO) exp = rb_int_plus(exp, fn); - *div = f_expt10(exp); } else { if (fn != ZERO) exp = rb_int_minus(exp, fn); - if (INT_NEGATIVE_P(exp)) { - *div = f_expt10(negate_num(exp)); - } - else { - if (exp != ZERO) *num = rb_int_mul(n, f_expt10(exp)); - *div = ONE; - } + exp = negate_num(exp); } + *nexp = exp; } } @@ -2414,22 +2407,21 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/rational.c#L2407 parse_rat(const char *s, const char *const e, int strict, int raise) { int sign; - VALUE num, den, ndiv, ddiv; + VALUE num, den, nexp, dexp; s = skip_ws(s, e); sign = read_sign(&s, e); - if (!read_num(&s, e, &num, &ndiv)) { + if (!read_num(&s, e, &num, &nexp)) { if (strict) return Qnil; return canonicalization ? ZERO : nurat_s_alloc(rb_cRational); } - nurat_reduce(&num, &ndiv); - den = ndiv; + den = ONE; if (s < e && *s == '/') { s++; - if (!read_num(&s, e, &den, &ddiv)) { + if (!read_num(&s, e, &den, &dexp)) { if (strict) return Qnil; - den = ndiv; + den = ONE; } else if (den == ZERO) { if (!raise) return Qnil; @@ -2439,17 +2431,38 @@ parse_rat(const char *s, const char *con https://github.com/ruby/ruby/blob/trunk/rational.c#L2431 return Qnil; } else { - nurat_reduce(&den, &ddiv); + nexp = rb_int_minus(nexp, dexp); nurat_reduce(&num, &den); - nurat_reduce(&ndiv, &ddiv); - if (ndiv != ONE) den = rb_int_mul(den, ndiv); - if (ddiv != ONE) num = rb_int_mul(num, ddiv); } } else if (strict && skip_ws(s, e) != e) { return Qnil; } + if (nexp != ZERO) { + if (INT_NEGATIVE_P(nexp)) { + VALUE mul; + if (!FIXNUM_P(nexp)) { + overflow: + return sign == '-' ? DBL2NUM(-HUGE_VAL) : DBL2NUM(HUGE_VAL); + } + mul = f_expt10(LONG2NUM(-FIX2LONG(nexp))); + if (RB_FLOAT_TYPE_P(mul)) goto overflow; + num = rb_int_mul(num, mul); + } + else { + VALUE div; + if (!FIXNUM_P(nexp)) { + underflow: + return sign == '-' ? DBL2NUM(-0.0) : DBL2NUM(+0.0); + } + div = f_expt10(nexp); + if (RB_FLOAT_TYPE_P(div)) goto underflow; + den = rb_int_mul(den, div); + } + nurat_reduce(&num, &den); + } + if (sign == '-') { num = negate_num(num); } @@ -2459,6 +2472,8 @@ parse_rat(const char *s, const char *con https://github.com/ruby/ruby/blob/trunk/rational.c#L2472 return num; } +#define FLOAT_ZERO_P(x) (rb_float_value(x) == 0.0) + static VALUE string_to_r_strict(VALUE self, int raise) { @@ -2473,7 +2488,7 @@ string_to_r_strict(VALUE self, int raise https://github.com/ruby/ruby/blob/trunk/rational.c#L2488 self); } - if (RB_FLOAT_TYPE_P(num)) { + if (RB_FLOAT_TYPE_P(num) && !FLOAT_ZERO_P(num)) { if (!raise) return Qnil; rb_raise(rb_eFloatDomainError, "Infinity"); } @@ -2517,7 +2532,7 @@ string_to_r(VALUE self) https://github.com/ruby/ruby/blob/trunk/rational.c#L2532 num = parse_rat(RSTRING_PTR(self), RSTRING_END(self), 0, TRUE); - if (RB_FLOAT_TYPE_P(num)) + if (RB_FLOAT_TYPE_P(num) && !FLOAT_ZERO_P(num)) rb_raise(rb_eFloatDomainError, "Infinity"); return num; } @@ -2529,7 +2544,7 @@ rb_cstr_to_rat(const char *s, int strict https://github.com/ruby/ruby/blob/trunk/rational.c#L2544 num = parse_rat(s, s + strlen(s), strict, TRUE); - if (RB_FLOAT_TYPE_P(num)) + if (RB_FLOAT_TYPE_P(num) && !FLOAT_ZERO_P(num)) rb_raise(rb_eFloatDomainError, "Infinity"); return num; } Index: test/ruby/test_rational.rb =================================================================== --- test/ruby/test_rational.rb (revision 65617) +++ test/ruby/test_rational.rb (revision 65618) @@ -120,6 +120,9 @@ class Rational_Test < Test::Unit::TestCa https://github.com/ruby/ruby/blob/trunk/test/ruby/test_rational.rb#L120 assert_raise_with_message(ArgumentError, /\u{221a 2668}/) { Rational("\u{221a 2668}") } + assert_warning('') { + assert_predicate(Rational('1e-99999999999999999999'), :zero?) + } assert_raise(TypeError){Rational(Object.new)} assert_raise(TypeError){Rational(Object.new, Object.new)} -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/