I am writing a program where time is important, and I just realized through a lot of debugging prints that my big holdup (80% of computing time) is converting a very large BigInteger (50K digits) into a string.
Is this behavior to be expected or how can I change something to make it run faster?
Converting numbers to strings is an expensive operation even if you use long and double.
Normally, the only thing more expensive is the IO you perform when writing the text for a file or the console.
It is worth noting that the built in converter a number to text is an O(N^2) operation where N is the number of digits. As such it is not surprising that 50K digit numbers take a very long time to convert to a decimal String.
Based on tmyklebu's suggestion I have written this. It is slower for numbers with less than 500 digits, but is much faster in the range of 50,000 digits.
public static void main(String... args) {
BigInteger bi = BigInteger.valueOf(11).pow(48100);
System.out.println(bi.toString());
System.out.println(toString(bi));
System.out.println("bi.length=" + bi.toString().length() + ", toString(bi).length=" + toString(bi).length());
for (int i = 0; i < 10; i++) {
long start = System.nanoTime();
String s = bi.toString();
long mid = System.nanoTime();
String s2 = toString(bi);
long end = System.nanoTime();
System.out.printf("time1 %.3f ms, time2 %.3f ms%n", (mid - start) / 1e6, (end - mid) / 1e6);
if (!s.equals(s2))
throw new AssertionError();
}
}
public static String toString(BigInteger bi) {
StringBuilder sb = new StringBuilder();
int i = 16;
while (bi.compareTo(powerOfTen(i)) > 0)
i *= 2;
toString(bi, sb, i);
int start = 0;
while (sb.charAt(start) == '0')
start++;
return sb.substring(start);
}
private static void toString(BigInteger bi, StringBuilder sb, int digits) {
if (digits < 18) {
int start = sb.length();
for (int i = 0; i < digits; i++)
sb.append('0');
long l = bi.longValue();
for (int i = digits - 1; i >= 0; i--, l /= 10)
sb.setCharAt(start + i, (char) ('0' + l % 10));
} else {
int digits2 = digits / 2;
BigInteger[] parts = bi.divideAndRemainder(powerOfTen(digits2));
toString(parts[0], sb, digits - digits2);
toString(parts[1], sb, digits2);
}
}
private static final Map<Integer, BigInteger> powersOfTen = new HashMap<Integer, BigInteger>();
private static BigInteger powerOfTen(int digits2) {
BigInteger tens = powersOfTen.get(digits2);
if (tens == null)
powersOfTen.put(digits2, tens = BigInteger.TEN.pow(digits2));
return tens;
}
prints
973096948397248203274473625697464617461138859359846077811290536......
973096948397248203274473625697464617461138859359846077811290536......
bi.length=50091, toString(bi).length=50091
time1 525.892 ms, time2 67.260 ms
time1 458.559 ms, time2 98.178 ms
time1 441.275 ms, time2 92.902 ms
time1 399.339 ms, time2 98.448 ms
time1 518.761 ms, time2 97.804 ms
time1 396.884 ms, time2 65.651 ms
time1 363.945 ms, time2 98.827 ms
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With