Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8317764

java.math.BigInteger#sqrt is slower than necessary

XMLWordPrintable

    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      This method is added since Java 9

      A DESCRIPTION OF THE PROBLEM :
      BigInteger#sqrt is slower than necessary (and harder to debug than necessary.)

      In method java.math.MutableBigInteger#sqrt, Newton's iteration is used to calculate square root of BigInteger. However, the initial guess is calculated wildly wrong, so the iteration is slower than necessary when BigInteger is big.

      This method is hard to debug because java.math.MutableBigInteger#toString implicity calls toBigInteger which calls getMagnitudeArray which silently set a new array to the field value. So this method may give different answer when stepping in a debug session than run directly.

      The initial guess is wrong because after the rightShift, xk.value isn't really fully "normalized" despite the "normalize" call -- the normalize method doesn't care about the field intLen. So, the BigInteger#doubleValue method returns a double which is too big.

      This method has another issue -- A double doesn't have enough precision to hold a full long value (64 bit), and Math.ceil(Math.sqrt(d)) doesn't always return approximation that IS LARGER THAN OR EQUAL TO the real value (especially that xk is right shifted and shifted back), which is IMPORTANT if xk1.compare(xk) >= 0 is used to terminate the while loop.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Calculate square root of "1606938044258990275541962092343697903722659452585786241712130", which is 2^200+2^101+2. Then set a break point at MutableBigInteger.java line 1933 (which is "double d = new BigInteger(xk.value, 1).doubleValue();") in IDE and watch d's value. Delete that break point and set a new break point at line 1934 and run again. The value of d becomes different (larger than max value of a long).

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Sqrt should be fast and correct and easy to debug.
      ACTUAL -
      Sqrt is slower than necessary, and hard to debug than necessary (when stepping in an IDE, it may yield different value)

      ---------- BEGIN SOURCE ----------
      new BigInteger("1606938044258990275541962092343697903722659452585786241712130").sqrt()

      This gives correct result but slower than necessary.
      When run step by step in a (typical) IDE, it gives wrong result.
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      No workaround needed because it is a performance issue.

      FREQUENCY : always


            rgiulietti Raffaello Giulietti
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: