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

ruby-changes:21091

From: marcandre <ko1@a...>
Date: Wed, 31 Aug 2011 13:13:09 +0900 (JST)
Subject: [ruby-changes:21091] marcandRe: r33140 (trunk): * numeric.c (flo_round): Avoid overflow by optimizing for trivial cases

marcandre	2011-08-31 13:13:00 +0900 (Wed, 31 Aug 2011)

  New Revision: 33140

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=33140

  Log:
    * numeric.c (flo_round): Avoid overflow by optimizing for trivial cases
      [Bug #5227]

  Modified files:
    trunk/ChangeLog
    trunk/numeric.c

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 33139)
+++ ChangeLog	(revision 33140)
@@ -1,3 +1,8 @@
+Wed Aug 31 13:09:41 2011  Marc-Andre Lafortune  <ruby-core@m...>
+
+	* numeric.c (flo_round): Avoid overflow by optimizing for trivial
+	  cases [Bug #5227]
+
 Wed Aug 31 00:50:01 2011  NAKAMURA Usaku  <usa@r...>
 
 	* win32/win32.c (rb_w32_select_with_thread): and my typo. we all must
Index: numeric.c
===================================================================
--- numeric.c	(revision 33139)
+++ numeric.c	(revision 33140)
@@ -1491,18 +1491,40 @@
     VALUE nd;
     double number, f;
     int ndigits = 0;
+    int binexp;
     long val;
 
     if (argc > 0 && rb_scan_args(argc, argv, "01", &nd) == 1) {
 	ndigits = NUM2INT(nd);
     }
     number  = RFLOAT_VALUE(num);
-    f = pow(10, abs(ndigits));
+    frexp (number , &binexp);
 
-    if (isinf(f)) {
-	if (ndigits < 0) number = 0;
+/* Let `exp` be such that `number` is written as:"0.#{digits}e#{exp}",
+   i.e. such that  10 ** (exp - 1) <= |number| < 10 ** exp
+   Recall that up to 17 digits can be needed to represent a double,
+   so if ndigits + exp >= 17, the intermediate value (number * 10 ** ndigits)
+   will be an integer and thus the result is the original number.
+   If ndigits + exp <= 0, the result is 0 or "1e#{exp}", so
+   if ndigits + exp < 0, the result is 0.
+   We have:
+	2 ** (binexp-1) <= |number| < 2 ** binexp
+	10 ** ((binexp-1)/log_2(10)) <= |number| < 10 ** (binexp/log_2(10))
+	If binexp >= 0, and since log_2(10) = 3.322259:
+	   10 ** (binexp/4 - 1) < |number| < 10 ** (binexp/3)
+	   binexp/4 <= exp <= binexp/3
+	If binexp <= 0, swap the /4 and the /3
+	So if ndigits + binexp/(4 or 3) >= 17, the result is number
+	If ndigits + binexp/(3 or 4) < 0 the result is 0
+*/
+    if (isinf(number) || isnan(number)) {
+	/* Do nothing */
     }
-    else {
+    else if ((long)ndigits * (4 - (binexp > 0)) + binexp < 0) {
+	number = 0;
+    }
+    else if (((long)ndigits - 17) * (3 + (binexp > 0)) + binexp < 0) {
+	f = pow(10, abs(ndigits));
 	if (ndigits < 0) {
 	    double absnum = fabs(number);
 	    if (absnum < f) return INT2FIX(0);

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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