ruby-changes:45640
From: nobu <ko1@a...>
Date: Sat, 25 Feb 2017 14:44:45 +0900 (JST)
Subject: [ruby-changes:45640] nobu:r57713 (trunk): bignum.c: improve estimate
nobu 2017-02-25 14:44:39 +0900 (Sat, 25 Feb 2017) New Revision: 57713 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=57713 Log: bignum.c: improve estimate * bignum.c (estimate_initial_sqrt, rb_big_isqrt): improve initial estimate by sqrt(). [ruby-core:79754] [Feature #13250] Modified files: trunk/bignum.c Index: bignum.c =================================================================== --- bignum.c (revision 57712) +++ bignum.c (revision 57713) @@ -6771,26 +6771,37 @@ BDIGIT rb_bdigit_dbl_isqrt(BDIGIT_DBL); https://github.com/ruby/ruby/blob/trunk/bignum.c#L6771 static BDIGIT * estimate_initial_sqrt(VALUE *xp, const size_t xn, const BDIGIT *nds, size_t len) { + enum {dbl_per_bdig = roomof(DBL_MANT_DIG,BITSPERDIG)}; const int zbits = nlz(nds[len-1]); - const int shift_bits = (len&1 ? BITSPERDIG/2 : BITSPERDIG) - (zbits+1)/2 + 1; - VALUE x = bignew_1(0, xn, 1); /* division may release the GVL */ + VALUE x = *xp = bignew_1(0, xn, 1); /* division may release the GVL */ BDIGIT *xds = BDIGITS(x); + BDIGIT_DBL d = bary2bdigitdbl(nds+len-dbl_per_bdig, dbl_per_bdig); + BDIGIT lowbits = 1; + int rshift = (int)((BITSPERDIG*2-zbits+(len&BITSPERDIG&1) - DBL_MANT_DIG + 1) & ~1); + double f; + + if (rshift > 0) { + lowbits = (BDIGIT)d & ~(~(BDIGIT)1U << rshift); + d >>= rshift; + } + else if (rshift < 0) { + d <<= -rshift; + d |= nds[len-dbl_per_bdig-1] >> (BITSPERDIG+rshift); + } + f = sqrt((double)d); + d = (BDIGIT_DBL)ceil(f); + if ((double)d == f) { + if (lowbits || (lowbits = !bary_zero_p(nds, len-dbl_per_bdig))) + ++d; + } + rshift /= 2; + rshift += (2-(len&1))*BITSPERDIG/2; + if (rshift >= 0) { + d <<= rshift; + } + bdigitdbl2bary(&xds[xn-2], 2, d); - *xp = x; - /* x = (n >> (b/2+1)) */ - if (shift_bits == BITSPERDIG) { - MEMCPY(xds, nds+len-xn, BDIGIT, xn); - } - else if (shift_bits > BITSPERDIG) { - bary_small_rshift(xds, nds+len-xn, xn, shift_bits-BITSPERDIG, 0); - } - else { - bary_small_rshift(xds, nds+len-xn-1, xn, shift_bits, nds[len-1]); - } - /* x |= (1 << (b-1)/2) */ - xds[xn-1] |= (BDIGIT)1u << - ((len&1 ? 0 : BITSPERDIG/2) + (BITSPERDIG-zbits-1)/2); - + if (!lowbits) return NULL; /* special case, exact result */ return xds; } @@ -6799,6 +6810,9 @@ rb_big_isqrt(VALUE n) https://github.com/ruby/ruby/blob/trunk/bignum.c#L6810 { BDIGIT *nds = BDIGITS(n); size_t len = BIGNUM_LEN(n); + size_t xn = (len+1) / 2; + VALUE x; + BDIGIT *xds; if (len <= 2) { BDIGIT sq = rb_bdigit_dbl_isqrt(bary2bdigitdbl(nds, len)); @@ -6808,29 +6822,27 @@ rb_big_isqrt(VALUE n) https://github.com/ruby/ruby/blob/trunk/bignum.c#L6822 return ULONG2NUM(sq); #endif } - else { - size_t tn = (len+1) / 2, xn = tn; - VALUE t, x; - BDIGIT *tds, *xds = estimate_initial_sqrt(&x, xn, nds, len); + else if ((xds = estimate_initial_sqrt(&x, xn, nds, len)) != 0) { + size_t tn = xn + BIGDIVREM_EXTRA_WORDS; + VALUE t = bignew_1(0, tn, 1); + BDIGIT *tds = BDIGITS(t); + tn = BIGNUM_LEN(t); /* t = n/x */ - tn += BIGDIVREM_EXTRA_WORDS; - t = bignew_1(0, tn, 1); - tds = BDIGITS(t); - tn = BIGNUM_LEN(t); while (bary_divmod_branch(tds, tn, NULL, 0, nds, len, xds, xn), bary_cmp(tds, tn, xds, xn) < 0) { int carry; BARY_TRUNC(tds, tn); + /* x = (x+t)/2 */ carry = bary_add(xds, xn, xds, xn, tds, tn); bary_small_rshift(xds, xds, xn, 1, carry); tn = BIGNUM_LEN(t); } rb_big_realloc(t, 0); rb_gc_force_recycle(t); - RBASIC_SET_CLASS_RAW(x, rb_cInteger); - return x; } + RBASIC_SET_CLASS_RAW(x, rb_cInteger); + return x; } /* -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/