From: matz@... (Yukihiro Matsumoto) Date: 2002-05-07T00:15:36+09:00 Subject: Re: Fixes for the new step methods in the range.c & numeric.c まつもと ゆきひろです 以下のようなメールがruby-talkに来たのですが、私にはどこがどう違うのか 良く分かりませんでした。もしよければ知恵を貸していただけませんか? In message "Fixes for the new step methods in the range.c & numeric.c" on 02/05/06, chr_rippel@gmx.net writes: | |This is a multi-part message in MIME format. | |------=_NextPart_000_0001_01C1F50B.6E551680 |Content-Type: text/plain; | charset="us-ascii" |Content-Transfer-Encoding: 7bit | |Hi, | |an included patch for the step method in range.c is a simple |bug-fix - for example | |--- |class Class | alias succ superclass |end | |# currently throws an exception complaining that succ for nil |# is undefined |(Fixnum..Object).step(2) {|x| p x } |--- | |The patch for the step method in numeric.c adds a sequence |end point adjustment strategy - #step_fuzzyness measures the |``fuzzyness'' of step-length additions around the end-point |and adds a safety margin (there exist probably much better |step_fuzzyness measures) - in pure Ruby: | |--- |class Float | def test_step(b,s) | n = ((b - self)/ s).round | last = n*s + self | | fuzzyness = step_fuzzyness(n,s) | if (s > 0 && last > b + fuzzyness) || \ | (s < 0 && last < b - fuzzyness) | n-=1 | last = n*s + self | end | | if (last - b).abs < fuzzyness | last = b | end | | (n-1).times {|i| yield i*s + self } | yield last | self | end | | EPSILON = 2.0**(-43) | def step_fuzzyness(n, s) | tmp = ((n-1)..(n+1)).collect do |i| | ((i*s + self - ((i-1)*s + self)) - s).abs | end | tmp.push s.abs*EPSILON | 2*tmp.max | end |end | | |# with this patch on my machine ... |M = 52343 |T = 12418.979 |K = 20300012 |L = 15673 | |p b = (K/T) # => 1634.595887 |p e = (-L*M + K)/T # => -64423.31749 |p s = -M/T # => -4.214758717 |p err = M/T * (2.0**(-40))# => 3.677147842e-12 | |# with this patch on my machine | |last = nil |b.step(e,s) {|last| } |p e - last # => 0.0 | |b.step(e+err,s) {|last| } |p (e+err) - last # => -4.214758717 | |b.step(e-err,s) {|last| } |p (e-err) - last # => -7.275957614e-12 | | |# a bigger error is necessary to demonstrate |# fuzzyness in pure Ruby | |p err = M/T * (2.0**(-38.00)) | |b.test_step(e,s) {|last| } |p e - last # => 0.0 | |b.test_step(e+err,s) {|last| } |p (e+err) - last # => -4.214758717 | |b.test_step(e-err,s) {|last| } |p (e-err) - last # => -1.455191523e-11 |--- | | |/Christoph | |------=_NextPart_000_0001_01C1F50B.6E551680 |Content-Type: application/octet-stream; | name="range.patch" |Content-Transfer-Encoding: quoted-printable |Content-Disposition: attachment; | filename="range.patch" | |--- range.c.1.34 Fri May 3 18:44:42 2002=0A= |+++ range.c Fri May 3 00:23:28 2002=0A= |@@ -71,7 +71,7 @@=0A= | VALUE obj;=0A= | {=0A= | VALUE beg, end, flags;=0A= |- =0A= |+=0A= | rb_scan_args(argc, argv, "21", &beg, &end, &flags);=0A= | /* Ranges are immutable, so that they should be initialized only = |once. */=0A= | if (rb_ivar_defined(obj, id_beg)) {=0A= |@@ -304,26 +304,21 @@=0A= | else { /* generic each */=0A= | VALUE v =3D b;=0A= | long lim =3D NUM2LONG(step);=0A= |- long i;=0A= |+ long i =3D lim;=0A= | =0A= | if (lim <=3D 0) {=0A= | rb_raise(rb_eArgError, "step can't be <=3D 0");=0A= | }=0A= |- if (EXCL(range)) {=0A= |- while (r_lt(v, e)) {=0A= |- if (r_eq(v, e)) break;=0A= |- rb_yield(v);=0A= |- for (i=3D0; i=3D 1.0e10) {=0A= | d1 =3D avalue;=0A= | while (d1 > 10.0) d1 /=3D 10.0;=0A= | d1 =3D modf(d1, &d2);=0A= | if (d1 =3D=3D 0) fmt =3D "%.1e";=0A= |- } =0A= |+ }=0A= | else if ((d1 =3D modf(value, &d2)) =3D=3D 0) {=0A= | fmt =3D "%.1f";=0A= | }=0A= |@@ -641,7 +641,7 @@=0A= | =0A= | static VALUE flo_is_nan_p(num)=0A= | VALUE num;=0A= |-{ =0A= |+{=0A= | =0A= | double value =3D RFLOAT(num)->value;=0A= | =0A= |@@ -650,7 +650,7 @@=0A= | =0A= | static VALUE flo_is_infinite_p(num)=0A= | VALUE num;=0A= |-{ =0A= |+{=0A= | double value =3D RFLOAT(num)->value;=0A= | =0A= | if (isinf(value)) {=0A= |@@ -662,12 +662,12 @@=0A= | =0A= | static VALUE flo_is_finite_p(num)=0A= | VALUE num;=0A= |-{ =0A= |+{=0A= | double value =3D RFLOAT(num)->value;=0A= | =0A= | if (isinf(value) || isnan(value))=0A= | return Qfalse;=0A= |- =0A= |+=0A= | return Qtrue;=0A= | }=0A= | =0A= |@@ -762,6 +762,25 @@=0A= | return flo_truncate(rb_Float(num));=0A= | }=0A= | =0A= |+=0A= |+static double=0A= |+step_fuzzyness(n,a,b,c)=0A= |+ long n;=0A= |+ double a,b,c;=0A= |+{=0A= |+ /* eps =3D 2^(-43) */=0A= |+ const double eps =3D 1.136868377e-13;=0A= |+ double n0 =3D (n-2)*c + a;=0A= |+ double n1 =3D (n-1)*c + a;=0A= |+ double n2 =3D (n)*c + a;=0A= |+ double n3 =3D ((n)+1)*c + a;=0A= |+ /* max-macro is not universal */=0A= |+ #define fuzzy_max(a,b) (((a) > (b)) ? (a) : (b))=0A= |+ return 2*fuzzy_max(fuzzy_max(fabs(n1-n0-c),fabs(n2-n1-c)),\=0A= |+ fuzzy_max(fabs(n3-n2-c),fabs(c)*eps));=0A= |+}=0A= |+=0A= |+=0A= | static VALUE=0A= | num_step(argc, argv, from)=0A= | int argc;=0A= |@@ -798,17 +817,32 @@=0A= | }=0A= | }=0A= | else if (TYPE(from) =3D=3D T_FLOAT || TYPE(to) =3D=3D T_FLOAT || = |TYPE(step) =3D=3D T_FLOAT) {=0A= |- const double epsilon =3D 2.2204460492503131E-16;=0A= | double beg =3D NUM2DBL(from);=0A= | double end =3D NUM2DBL(to);=0A= | double unit =3D NUM2DBL(step);=0A= |- double n =3D (end - beg)/unit;=0A= |- long i;=0A= |+ double fuzzy,last;=0A= |+ long n,i;=0A= |+ n =3D floor((end - beg)/unit + 0.5);=0A= |+=0A= |+ if (n < 0) {=0A= |+ return from;=0A= |+ }=0A= |+ fuzzy =3D step_fuzzyness(n,beg,end,unit);=0A= |+ last =3D n* unit + beg;=0A= | =0A= |- n =3D floor(n + n*epsilon) + 1;=0A= |- for (i=3D0; i 0.0 && last > end + fuzzy) || (unit < 0.0 && last < end - = |fuzzy)) {=0A= |+ n--;=0A= |+ last =3D n*unit + beg;=0A= | }=0A= |+ if (fabs(last - end) < fuzzy) {=0A= |+ last =3D end;=0A= |+ }=0A= |+=0A= |+ for (i =3D 0; i