More precise Bignum#to_f

e$B$J$+$@$G$9!#e(B

e$B$A$g$C$HIbF0>.?tE@4X78e(B(e$B@53N$K$O4]$a4X78e(B)e$B$r8+D>$7$F$$$?$H$3$m!"e(B
2007-07-28 e$B$NLdBj$,%j%F%i%k0J30$G$OD>$Ce(B
e$B$F$$$J$$$3$H$K5$$E$-$^$7$?!#e(B

e$B0J2<$N%Q%C%A$G$O!"?te(B%e$B$+$ie(B10%e$B6/DxEYCY$/$J$k$h$&$G$9!#e(B

x=253+1
a = []
a.concat (0…33).map{|i|[(x << i) + 1, (x+1) << i]}
x=2
23+1
a.concat (7…33).map{|i|[(x << i) + 1, (x << i) + 1]}

a.each {|i,j| p i unless i.to_f==j}
t0 = Process.times
100000.times{a.each{|i,j| i.to_f}}
printf “%10.6f\n”, Process.times.utime-t0.utime

Index: bignum.c

— bignum.c (revision 15877)
+++ bignum.c (working copy)
@@ -1134,13 +1134,62 @@ rb_dbl2big(double d)
}

+static int
+nlz(BDIGIT x)
+{

  • BDIGIT y;
  • int n = BITSPERDIG;
    +#if BITSPERDIG > 64
  • y = x >> 64; if (y) {n -= 64; x = y;}
    +#endif
    +#if BITSPERDIG > 32
  • y = x >> 32; if (y) {n -= 32; x = y;}
    +#endif
    +#if BITSPERDIG > 16
  • y = x >> 16; if (y) {n -= 16; x = y;}
    +#endif
  • y = x >> 8; if (y) {n -= 8; x = y;}
  • y = x >> 4; if (y) {n -= 4; x = y;}
  • y = x >> 2; if (y) {n -= 2; x = y;}
  • y = x >> 1; if (y) {return n - 2;}
  • return n - x;
    +}

static double
big2dbl(VALUE x)
{
double d = 0.0;

  • long i = RBIGNUM_LEN(x);
  • BDIGIT *ds = BDIGITS(x);
  • long i = RBIGNUM_LEN(x), lo = 0, bits;
  • BDIGIT *ds = BDIGITS(x), dl;
  • while (i–) {
  • d = ds[i] + BIGRAD*d;
  • if (i) {
  • bits = i * BITSPERDIG - nlz(ds[i-1]);
  • if (bits > DBL_MANT_DIG+DBL_MAX_EXP) {
  •  d = HUGE_VAL;
    
  • }
  • else {
  •  if (bits > DBL_MANT_DIG+1)
    
  • lo = (bits -= DBL_MANT_DIG+1) / BITSPERDIG;
  •  else
    
  • bits = 0;
  •  while (--i > lo) {
    
  • d = ds[i] + BIGRAD*d;
  •  }
    
  •  dl = ds[i];
    
  •  if (bits && (dl & (1UL << (bits %= BITSPERDIG)))) {
    
  • int carry = dl & ~(~0UL << bits);
  • if (!carry) {
  •    while (i-- > 0) {
    
  •  if ((carry = ds[i]) != 0) break;
    
  •    }
    
  • }
  • if (carry) {
  •    dl &= ~0UL << bits;
    
  •    dl += 1UL << bits;
    
  •    if (!dl) d += 1;
    
  • }
  •  }
    
  •  d = dl + BIGRAD*d;
    
  •  if (lo) d = ldexp(d, lo * BITSPERDIG);
    
  • }
    }
    if (!RBIGNUM_SIGN(x)) d = -d;

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:34195] more precise Bignum#to_f”
on Tue, 1 Apr 2008 15:43:33 +0900, Nobuyoshi N.
[email protected] writes:

|e$B$A$g$C$HIbF0>.?tE@4X78e(B(e$B@53N$K$O4]$a4X78e(B)e$B$r8+D>$7$F$$$?$H$3$m!“e(B
|http://d.hatena.ne.jp/hnw/20070728 e$B$NLdBj$,%j%F%i%k0J30$G$OD>$Ce(B
|e$B$F$$$J$$$3$H$K5$$E$-$^$7$?!#e(B
|
|e$B0J2<$N%Q%C%A$G$O!”?te(B%e$B$+$ie(B10%e$B6/DxEYCY$/$J$k$h$&$G$9!#e(B

e$B%3%_%C%H$7$F$/$@$5$$!#e(B