diff options
author | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-11-08 02:25:44 +0000 |
---|---|---|
committer | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-11-08 02:25:44 +0000 |
commit | 17e9667fe9707ff5f20cc320cbd111f886caa5b9 (patch) | |
tree | b98a66a057626de6f856e0229554ea9f31722cfc | |
parent | dcfb7f6d541ee7e80f6a05726e533d4b75a4b785 (diff) | |
download | ruby-17e9667fe9707ff5f20cc320cbd111f886caa5b9.tar.gz |
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.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65618 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | rational.c | 65 | ||||
-rw-r--r-- | test/ruby/test_rational.rb | 3 |
2 files changed, 43 insertions, 25 deletions
diff --git a/rational.c b/rational.c index 9712ce72bb..7f3b9b08c7 100644 --- a/rational.c +++ b/rational.c @@ -2336,13 +2336,13 @@ negate_num(VALUE num) } 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 *const end, VALUE *num, VALUE *div) 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 *const end, VALUE *num, VALUE *div) 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 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 *const e, int strict, int raise) 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 *const e, int strict, int raise) 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) 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) 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) /* for complex's internal */ 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; } diff --git a/test/ruby/test_rational.rb b/test/ruby/test_rational.rb index c44ff0393a..b289753347 100644 --- a/test/ruby/test_rational.rb +++ b/test/ruby/test_rational.rb @@ -120,6 +120,9 @@ class Rational_Test < Test::Unit::TestCase 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)} |