| 258 | } |
| 259 | |
| 260 | @GwtIncompatible // TODO |
| 261 | private static BigInteger sqrtFloor(BigInteger x) { |
| 262 | /* |
| 263 | * Adapted from Hacker's Delight, Figure 11-1. |
| 264 | * |
| 265 | * Using DoubleUtils.bigToDouble, getting a double approximation of x is extremely fast, and |
| 266 | * then we can get a double approximation of the square root. Then, we iteratively improve this |
| 267 | * guess with an application of Newton's method, which sets guess := (guess + (x / guess)) / 2. |
| 268 | * This iteration has the following two properties: |
| 269 | * |
| 270 | * a) every iteration (except potentially the first) has guess >= floor(sqrt(x)). This is |
| 271 | * because guess' is the arithmetic mean of guess and x / guess, sqrt(x) is the geometric mean, |
| 272 | * and the arithmetic mean is always higher than the geometric mean. |
| 273 | * |
| 274 | * b) this iteration converges to floor(sqrt(x)). In fact, the number of correct digits doubles |
| 275 | * with each iteration, so this algorithm takes O(log(digits)) iterations. |
| 276 | * |
| 277 | * We start out with a double-precision approximation, which may be higher or lower than the |
| 278 | * true value. Therefore, we perform at least one Newton iteration to get a guess that's |
| 279 | * definitely >= floor(sqrt(x)), and then continue the iteration until we reach a fixed point. |
| 280 | */ |
| 281 | BigInteger sqrt0; |
| 282 | int log2 = log2(x, FLOOR); |
| 283 | if (log2 < Double.MAX_EXPONENT) { |
| 284 | sqrt0 = sqrtApproxWithDoubles(x); |
| 285 | } else { |
| 286 | int shift = (log2 - DoubleUtils.SIGNIFICAND_BITS) & ~1; // even! |
| 287 | /* |
| 288 | * We have that x / 2^shift < 2^54. Our initial approximation to sqrtFloor(x) will be |
| 289 | * 2^(shift/2) * sqrtApproxWithDoubles(x / 2^shift). |
| 290 | */ |
| 291 | sqrt0 = sqrtApproxWithDoubles(x.shiftRight(shift)).shiftLeft(shift >> 1); |
| 292 | } |
| 293 | BigInteger sqrt1 = sqrt0.add(x.divide(sqrt0)).shiftRight(1); |
| 294 | if (sqrt0.equals(sqrt1)) { |
| 295 | return sqrt0; |
| 296 | } |
| 297 | do { |
| 298 | sqrt0 = sqrt1; |
| 299 | sqrt1 = sqrt0.add(x.divide(sqrt0)).shiftRight(1); |
| 300 | } while (sqrt1.compareTo(sqrt0) < 0); |
| 301 | return sqrt0; |
| 302 | } |
| 303 | |
| 304 | @GwtIncompatible // TODO |
| 305 | private static BigInteger sqrtApproxWithDoubles(BigInteger x) { |