--- old/src/java.base/share/classes/java/math/BigInteger.java 2019-08-22 10:36:10.000000000 -0700 +++ new/src/java.base/share/classes/java/math/BigInteger.java 2019-08-22 10:36:09.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -3937,32 +3937,67 @@ if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) radix = 10; + // Ensure buffer capacity sufficient to contain string representation + // floor(bitLength*log(2)/log(radix)) + 1 + // plus an additional character for the sign if negative. + int b = this.abs().bitLength(); + int numChars = (int)(Math.floor(b*LOG_TWO/logCache[radix]) + 1) + + (signum < 0 ? 1 : 0); + StringBuilder sb = new StringBuilder(numChars); + // If it's small enough, use smallToString. - if (mag.length <= SCHOENHAGE_BASE_CONVERSION_THRESHOLD) - return smallToString(radix); + if (mag.length <= SCHOENHAGE_BASE_CONVERSION_THRESHOLD) { + // smallToString() will prepend a negative sign if this < 0, + // but will not pad with any leading zeros. + smallToString(radix, sb, -1); + return sb.toString(); + } - // Otherwise use recursive toString, which requires positive arguments. + // Otherwise use recursive toString. // The results will be concatenated into this StringBuilder - StringBuilder sb = new StringBuilder(); - if (signum < 0) { - toString(this.negate(), sb, radix, 0); - sb.insert(0, '-'); - } - else - toString(this, sb, radix, 0); + toString(this, sb, radix, 0); return sb.toString(); } - /** This method is used to perform toString when arguments are small. */ - private String smallToString(int radix) { + private static void padWithZeros(StringBuilder buf, int len, int digits) { + if (digits > 0 && len < digits) { + int m = digits - len; + while (m >= NUM_ZEROS) { + buf.append(ZEROS); + m -= NUM_ZEROS; + } + if (m > 0) { + buf.append(ZEROS, 0, m); + } + } + } + + /** + * This method is used to perform toString when arguments are small. + * A negative sign will be prepended if and only if {@code this < 0}. + * If {@code digits <= 0} no padding (pre-pending with zeros) will be + * effected. + * + * @param radix The base to convert to. + * @param sb The StringBuilder that will be appended to in place. + * @param digits The minimum number of digits to pad to. + */ + private void smallToString(int radix, StringBuilder buf, int digits) { if (signum == 0) { - return "0"; + buf.append('0'); + padWithZeros(buf, 1, digits); + return; + } + + // Put sign (if any) into result buffer + if (signum < 0) { + buf.append('-'); } // Compute upper bound on number of digit groups and allocate space int maxNumDigitGroups = (4*mag.length + 6)/7; - String digitGroup[] = new String[maxNumDigitGroups]; + long[] digitGroups = new long[maxNumDigitGroups]; // Translate number to string, a digit group at a time BigInteger tmp = this.abs(); @@ -3977,27 +4012,30 @@ BigInteger q2 = q.toBigInteger(tmp.signum * d.signum); BigInteger r2 = r.toBigInteger(tmp.signum * d.signum); - digitGroup[numGroups++] = Long.toString(r2.longValue(), radix); + digitGroups[numGroups++] = r2.longValue(); tmp = q2; } - // Put sign (if any) and first digit group into result buffer - StringBuilder buf = new StringBuilder(numGroups*digitsPerLong[radix]+1); - if (signum < 0) { - buf.append('-'); - } - buf.append(digitGroup[numGroups-1]); + // Get string version of first digit group + String s = Long.toString(digitGroups[numGroups-1], radix); + + // Pad with internal zeros if necessary. + padWithZeros(buf, s.length() + (numGroups - 1)*digitsPerLong[radix], + digits); + + // Put first digit group into result buffer + buf.append(s); - // Append remaining digit groups padded with leading zeros + // Append remaining digit groups each padded with leading zeros for (int i=numGroups-2; i >= 0; i--) { // Prepend (any) leading zeros for this digit group - int numLeadingZeros = digitsPerLong[radix]-digitGroup[i].length(); + s = Long.toString(digitGroups[i], radix); + int numLeadingZeros = digitsPerLong[radix] - s.length(); if (numLeadingZeros != 0) { - buf.append(zeros[numLeadingZeros]); + buf.append(ZEROS, 0, numLeadingZeros); } - buf.append(digitGroup[i]); + buf.append(s); } - return buf.toString(); } /** @@ -4013,32 +4051,33 @@ * @param radix The base to convert to. * @param digits The minimum number of digits to pad to. */ - private static void toString(BigInteger u, StringBuilder sb, int radix, - int digits) { + private static void toString(BigInteger u, StringBuilder sb, + int radix, int digits) { + // We're at the beginning if nothing is in the StringBuilder. + boolean atBeginning = sb.length() == 0; + + // Make negative values positive and prepend a negative sign. + if (u.signum() < 0) { + u = u.negate(); + sb.append('-'); + } + // If we're smaller than a certain threshold, use the smallToString - // method, padding with leading zeroes when necessary. + // method, padding with leading zeroes when necessary unless we're + // at the beginning of the string or digits <= 0. As u.signum() >= 0, + // smallToString() will not prepend a negative sign. if (u.mag.length <= SCHOENHAGE_BASE_CONVERSION_THRESHOLD) { - String s = u.smallToString(radix); - - // Pad with internal zeros if necessary. - // Don't pad if we're at the beginning of the string. - if ((s.length() < digits) && (sb.length() > 0)) { - for (int i=s.length(); i < digits; i++) { - sb.append('0'); - } - } - - sb.append(s); + u.smallToString(radix, sb, atBeginning ? -1 : digits); return; } - int b, n; - b = u.bitLength(); - // Calculate a value for n in the equation radix^(2^n) = u // and subtract 1 from that value. This is used to find the // cache index that contains the best value to divide u. - n = (int) Math.round(Math.log(b * LOG_TWO / logCache[radix]) / LOG_TWO - 1.0); + int b = u.bitLength(); + int n = (int) Math.round(Math.log(b * LOG_TWO / logCache[radix]) / + LOG_TWO - 1.0); + BigInteger v = getRadixConversionCache(radix, n); BigInteger[] results; results = u.divideAndRemainder(v); @@ -4078,14 +4117,11 @@ return cacheLine[exponent]; } - /* zero[i] is a string of i consecutive zeros. */ - private static String zeros[] = new String[64]; - static { - zeros[63] = - "000000000000000000000000000000000000000000000000000000000000000"; - for (int i=0; i < 63; i++) - zeros[i] = zeros[63].substring(0, i); - } + /* Size of ZEROS string. */ + private static int NUM_ZEROS = 63; + + /* ZEROS is a string of NUM_ZEROS consecutive zeros. */ + private static final String ZEROS = "0".repeat(NUM_ZEROS); /** * Returns the decimal String representation of this BigInteger.