Hi

On Thu, Aug 25, 2011 at 9:46 PM, Yui NARUSE <naruse / airemix.jp> wrote:
> r33061 tried to fix this but the result isn't changed.

Sorry, I do not understand. r33061 fixes Integer#round, which had a
completely different issue from Float#round (see redmine #5228)

> Additional to say:
> * add test for this to test/ruby/test_float.rb

I already committed tests in RubySpec, which I believe is where they
are the most useful.

> * write Ticket number in ChangeLog and commit message
> =A0(this is current limitation of Redmine-commit association)

Ah, thanks, I didn't realize this had changed. I updated the
contributer guidelines wiki page (
http://redmine.ruby-lang.org/projects/ruby/wiki/CommitterHowto ) but I
don't have write access to another that should be updated accordingly:
  http://redmine.ruby-lang.org/projects/redmine/wiki/VersionControlSystem

Can someone either give me access or update it?

Thanks

> ----------------------------------------
> Bug #5227: Float#round fails on corner cases
> http://redmine.ruby-lang.org/issues/5227
>
> Author: Marc-Andre Lafortune
> Status: Assigned
> Priority: Normal
> Assignee: Marc-Andre Lafortune
> Category: core
> Target version: 1.9.3
> ruby -v: r32601
>
>
> Float#round fails on some corner cases:
>
> =A042.0.round(300) # =3D> 42.0
> =A042.0.round(308) # =3D> Infinity, should be 42.0
> =A042.0.round(309) # =3D> 42.0
>
> =A01.0e307.round(1) # =3D> 1.0e307
> =A01.0e307.round(2) # =3D> Infinity, should be 1.0e307
>
> These occur when the exponent of the intermediate value overflows.
>
> The original code already had criteria for extreme values, but we can fin=
d much tighter ones, as explained in the patch below. This fixes the bugs a=
bove and optimizes for most trivial cases.
>
> I'd be grateful if someone could look it over before I commit it, thanks.
>
>
> diff --git a/numeric.c b/numeric.c
> index 272bbd1..22608c9 100644
> --- a/numeric.c
> +++ b/numeric.c
> @@ -1491,18 +1491,37 @@ flo_round(int argc, VALUE *argv, VALUE num)
> =A0 =A0 VALUE nd;
> =A0 =A0 double number, f;
> =A0 =A0 int ndigits =3D 0;
> + =A0 =A0int binexp;
> =A0 =A0 long val;
>
> =A0 =A0 if (argc > 0 && rb_scan_args(argc, argv, "01", &nd) =3D=3D 1) {
> =A0 =A0 =A0 =A0ndigits =3D NUM2INT(nd);
> =A0 =A0 }
> =A0 =A0 number =A0=3D RFLOAT_VALUE(num);
> - =A0 =A0f =3D pow(10, abs(ndigits));
> -
> - =A0 =A0if (isinf(f)) {
> - =A0 =A0 =A0 if (ndigits < 0) number =3D 0;
> - =A0 =A0}
> - =A0 =A0else {
> + =A0 =A0frexp (number , &binexp);
> +
> +/* Let `exp` be such that `number` is written as: "0.#{digits}e#{exp}",
> + =A0 i.e. such that =A010 ** (exp - 1) <=3D |number| < 10 ** exp
> + =A0 Recall that up to 17 digits can be needed to represent a double,
> + =A0 so if ndigits + exp >=3D 17, the intermediate value (number * 10 **=
 ndigits)
> + =A0 will be an integer and thus the result is the original number.
> + =A0 If ndigits + exp <=3D 0, the result is 0 or "1e#{exp}", so
> + =A0 if ndigits + exp < 0, the result is 0.
> + =A0 We have:
> + =A0 =A0 =A0 2 ** (binexp-1) <=3D |number| < 2 ** binexp
> + =A0 =A0 =A0 10 ** ((binexp-1)/log_2(10)) <=3D |number| < 10 ** (binexp/=
log_2(10))
> + =A0 =A0 =A0 If binexp >=3D 0, and since log_2(10) =3D 3.322259:
> + =A0 =A0 =A0 =A0 =A010 ** (binexp/4 - 1) < |number| < 10 ** (binexp/3)
> + =A0 =A0 =A0 =A0 =A0binexp/4 <=3D exp <=3D binexp/3
> + =A0 =A0 =A0 If binexp <=3D 0, swap the /4 and the /3
> + =A0 =A0 =A0 So if ndigits + binexp/(3 or 4) >=3D 17, the result is numb=
er
> + =A0 =A0 =A0 If ndigits + binexp/(4 or 3) < 0 the result is 0
> +*/
> + =A0 =A0if ((long)ndigits * (4 - (binexp < 0)) + binexp < 0) {
> + =A0 =A0 =A0 number =3D 0;
> + =A0 =A0}
> + =A0 =A0else if ((long)(ndigits - 17) * (3 + (binexp < 0)) + binexp < 0)=
 {
> + =A0 =A0 =A0 f =3D pow(10, abs(ndigits));
> =A0 =A0 =A0 =A0if (ndigits < 0) {
> =A0 =A0 =A0 =A0 =A0 =A0double absnum =3D fabs(number);
> =A0 =A0 =A0 =A0 =A0 =A0if (absnum < f) return INT2FIX(0);
>
>
>
> --
> http://redmine.ruby-lang.org
>
>