ruby-changes:21092
From: marcandre <ko1@a...>
Date: Wed, 31 Aug 2011 13:21:03 +0900 (JST)
Subject: [ruby-changes:21092] marcandRe: r33141 (ruby_1_9_3): * backport r33140 from trunk
marcandre 2011-08-31 13:20:53 +0900 (Wed, 31 Aug 2011) New Revision: 33141 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=33141 Log: * backport r33140 from trunk * numeric.c (flo_round): Avoid overflow by optimizing for trivial cases [Bug #5227] Modified files: branches/ruby_1_9_3/ChangeLog branches/ruby_1_9_3/numeric.c Index: ruby_1_9_3/ChangeLog =================================================================== --- ruby_1_9_3/ChangeLog (revision 33140) +++ ruby_1_9_3/ChangeLog (revision 33141) @@ -1,3 +1,9 @@ +Wed Aug 31 13:19:31 2011 Marc-Andre Lafortune <ruby-core@m...> + + * numeric.c (flo_round): Avoid overflow by optimizing for trivial + cases + [Bug #5227] + Wed Aug 31 06:45:32 2011 KOSAKI Motohiro <kosaki.motohiro@g...> * configure.in: fix r32835. $withval can't be used outer AC_ARG_WITH(). Index: ruby_1_9_3/numeric.c =================================================================== --- ruby_1_9_3/numeric.c (revision 33140) +++ ruby_1_9_3/numeric.c (revision 33141) @@ -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/