[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]