Skip to content

Commit 0ff2c7f

Browse files
tompngnobu
authored andcommitted
Faster Integer.sqrt for large bignum
Integer.sqrt uses Newton's method. This pull request reduces the precision which was unnecessarily high in each calculation step.
1 parent dcfbe36 commit 0ff2c7f

File tree

3 files changed

+14
-71
lines changed

3 files changed

+14
-71
lines changed

bignum.c

Lines changed: 12 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -6878,63 +6878,11 @@ BDIGIT rb_bdigit_dbl_isqrt(BDIGIT_DBL);
68786878
# define BDIGIT_DBL_TO_DOUBLE(n) (double)(n)
68796879
#endif
68806880

6881-
static BDIGIT *
6882-
estimate_initial_sqrt(VALUE *xp, const size_t xn, const BDIGIT *nds, size_t len)
6883-
{
6884-
enum {dbl_per_bdig = roomof(DBL_MANT_DIG,BITSPERDIG)};
6885-
const int zbits = nlz(nds[len-1]);
6886-
VALUE x = *xp = bignew_1(0, xn, 1); /* division may release the GVL */
6887-
BDIGIT *xds = BDIGITS(x);
6888-
BDIGIT_DBL d = bary2bdigitdbl(nds+len-dbl_per_bdig, dbl_per_bdig);
6889-
BDIGIT lowbits = 1;
6890-
int rshift = (int)((BITSPERDIG*2-zbits+(len&BITSPERDIG&1) - DBL_MANT_DIG + 1) & ~1);
6891-
double f;
6892-
6893-
if (rshift > 0) {
6894-
lowbits = (BDIGIT)d & ~(~(BDIGIT)1U << rshift);
6895-
d >>= rshift;
6896-
}
6897-
else if (rshift < 0) {
6898-
d <<= -rshift;
6899-
d |= nds[len-dbl_per_bdig-1] >> (BITSPERDIG+rshift);
6900-
}
6901-
f = sqrt(BDIGIT_DBL_TO_DOUBLE(d));
6902-
d = (BDIGIT_DBL)ceil(f);
6903-
if (BDIGIT_DBL_TO_DOUBLE(d) == f) {
6904-
if (lowbits || (lowbits = !bary_zero_p(nds, len-dbl_per_bdig)))
6905-
++d;
6906-
}
6907-
else {
6908-
lowbits = 1;
6909-
}
6910-
rshift /= 2;
6911-
rshift += (2-(len&1))*BITSPERDIG/2;
6912-
if (rshift >= 0) {
6913-
if (nlz((BDIGIT)d) + rshift >= BITSPERDIG) {
6914-
/* (d << rshift) does cause overflow.
6915-
* example: Integer.sqrt(0xffff_ffff_ffff_ffff ** 2)
6916-
*/
6917-
d = ~(BDIGIT_DBL)0;
6918-
}
6919-
else {
6920-
d <<= rshift;
6921-
}
6922-
}
6923-
BDIGITS_ZERO(xds, xn-2);
6924-
bdigitdbl2bary(&xds[xn-2], 2, d);
6925-
6926-
if (!lowbits) return NULL; /* special case, exact result */
6927-
return xds;
6928-
}
6929-
69306881
VALUE
69316882
rb_big_isqrt(VALUE n)
69326883
{
69336884
BDIGIT *nds = BDIGITS(n);
69346885
size_t len = BIGNUM_LEN(n);
6935-
size_t xn = (len+1) / 2;
6936-
VALUE x;
6937-
BDIGIT *xds;
69386886

69396887
if (len <= 2) {
69406888
BDIGIT sq = rb_bdigit_dbl_isqrt(bary2bdigitdbl(nds, len));
@@ -6944,25 +6892,19 @@ rb_big_isqrt(VALUE n)
69446892
return ULONG2NUM(sq);
69456893
#endif
69466894
}
6947-
else if ((xds = estimate_initial_sqrt(&x, xn, nds, len)) != 0) {
6948-
size_t tn = xn + BIGDIVREM_EXTRA_WORDS;
6949-
VALUE t = bignew_1(0, tn, 1);
6950-
BDIGIT *tds = BDIGITS(t);
6951-
tn = BIGNUM_LEN(t);
6952-
6953-
/* t = n/x */
6954-
while (bary_divmod_branch(tds, tn, NULL, 0, nds, len, xds, xn),
6955-
bary_cmp(tds, tn, xds, xn) < 0) {
6956-
int carry;
6957-
BARY_TRUNC(tds, tn);
6958-
/* x = (x+t)/2 */
6959-
carry = bary_add(xds, xn, xds, xn, tds, tn);
6960-
bary_small_rshift(xds, xds, xn, 1, carry);
6961-
tn = BIGNUM_LEN(t);
6962-
}
6895+
else {
6896+
size_t shift = FIX2LONG(rb_big_bit_length(n)) / 4;
6897+
VALUE n2 = rb_int_rshift(n, SIZET2NUM(2 * shift));
6898+
VALUE x = FIXNUM_P(n2) ? LONG2FIX(rb_ulong_isqrt(FIX2ULONG(n2))) : rb_big_isqrt(n2);
6899+
/* x = (x+n/x)/2 */
6900+
x = rb_int_plus(rb_int_lshift(x, SIZET2NUM(shift - 1)), rb_int_idiv(rb_int_rshift(n, SIZET2NUM(shift + 1)), x));
6901+
VALUE xx = rb_int_mul(x, x);
6902+
while (rb_int_gt(xx, n)) {
6903+
xx = rb_int_minus(xx, rb_int_minus(rb_int_plus(x, x), INT2FIX(1)));
6904+
x = rb_int_minus(x, INT2FIX(1));
6905+
}
6906+
return x;
69636907
}
6964-
RBASIC_SET_CLASS_RAW(x, rb_cInteger);
6965-
return x;
69666908
}
69676909

69686910
#if USE_GMP

internal/numeric.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ VALUE rb_int_equal(VALUE x, VALUE y);
8686
VALUE rb_int_divmod(VALUE x, VALUE y);
8787
VALUE rb_int_and(VALUE x, VALUE y);
8888
VALUE rb_int_lshift(VALUE x, VALUE y);
89+
VALUE rb_int_rshift(VALUE x, VALUE y);
8990
VALUE rb_int_div(VALUE x, VALUE y);
9091
int rb_int_positive_p(VALUE num);
9192
int rb_int_negative_p(VALUE num);

numeric.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5169,7 +5169,7 @@ fix_rshift(long val, unsigned long i)
51695169
*
51705170
*/
51715171

5172-
static VALUE
5172+
VALUE
51735173
rb_int_rshift(VALUE x, VALUE y)
51745174
{
51755175
if (FIXNUM_P(x)) {

0 commit comments

Comments
 (0)