diff options
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java')
-rw-r--r-- | src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java | 732 |
1 files changed, 476 insertions, 256 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java index 4601cce9..2599a3a5 100644 --- a/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java +++ b/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.utilities; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2013 + Copyright (C) 2014 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.Formatter; +import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -56,6 +57,11 @@ public final class FormatUtil { public static final NumberFormat NF2 = NumberFormat.getInstance(Locale.US); /** + * Number Formatter (3 digits) for output purposes. + */ + public static final NumberFormat NF3 = NumberFormat.getInstance(Locale.US); + + /** * Number Formatter (4 digits) for output purposes. */ public static final NumberFormat NF4 = NumberFormat.getInstance(Locale.US); @@ -80,6 +86,9 @@ public final class FormatUtil { NF2.setMinimumFractionDigits(2); NF2.setMaximumFractionDigits(2); NF2.setGroupingUsed(false); + NF3.setMinimumFractionDigits(3); + NF3.setMaximumFractionDigits(3); + NF3.setGroupingUsed(false); NF4.setMinimumFractionDigits(4); NF4.setMaximumFractionDigits(4); NF4.setGroupingUsed(false); @@ -122,39 +131,34 @@ public final class FormatUtil { private static final int[] TIME_UNIT_DIGITS = new int[] { 3, 2, 2, 2, 2 }; /** - * Formats the double d with the specified fraction digits. + * Initialize a number format with ELKI standard options (US locale, no + * grouping). * - * @param d the double array to be formatted - * @param digits the number of fraction digits - * @return a String representing the double d - */ - public static String format(final double d, int digits) { + * @param digits Number of digits to use + * @return Number format + */ + public static NumberFormat makeNumberFormat(int digits) { + // Prefer predefined number formats where applicable. + // TODO: cache others, too? + switch(digits){ + case 0: + return NF0; + case 2: + return NF2; + case 3: + return NF3; + case 4: + return NF4; + case 6: + return NF6; + case 8: + return NF8; + } final NumberFormat nf = NumberFormat.getInstance(Locale.US); nf.setMaximumFractionDigits(digits); nf.setMinimumFractionDigits(digits); nf.setGroupingUsed(false); - return nf.format(d); - } - - /** - * Formats the double d with the specified number format. - * - * @param d the double array to be formatted - * @param nf the number format to be used for formatting - * @return a String representing the double d - */ - public static String format(final double d, NumberFormat nf) { - return nf.format(d); - } - - /** - * Formats the double d with 2 fraction digits. - * - * @param d the double to be formatted - * @return a String representing the double d - */ - public static String format(final double d) { - return format(d, 2); + return nf; } /** @@ -166,50 +170,37 @@ public final class FormatUtil { * @return a String representing the double array d */ public static String format(double[] d, String sep) { - StringBuilder buffer = new StringBuilder(); - for(int i = 0; i < d.length; i++) { - if(i > 0) { - buffer.append(sep).append(d[i]); - } - else { - buffer.append(d[i]); - } + if(d.length == 0) { + return ""; } - return buffer.toString(); + return formatTo(new StringBuilder(), d, sep).toString(); } /** - * Formats the double array d with the specified separator and the specified - * fraction digits. + * Formats the double array d with the specified number format. * * @param d the double array to be formatted - * @param sep the separator between the single values of the double array, - * e.g. ',' - * @param digits the number of fraction digits + * @param nf the number format to be used for formatting * @return a String representing the double array d */ - public static String format(double[] d, String sep, int digits) { - StringBuilder buffer = new StringBuilder(); - for(int i = 0; i < d.length; i++) { - if(i < d.length - 1) { - buffer.append(format(d[i], digits)).append(sep); - } - else { - buffer.append(format(d[i], digits)); - } - } - return buffer.toString(); + public static String format(double[] d, NumberFormat nf) { + return format(d, " ", nf); } /** * Formats the double array d with the specified number format. * * @param d the double array to be formatted + * @param sep the separator between the single values of the double array, + * e.g. ',' * @param nf the number format to be used for formatting * @return a String representing the double array d */ - public static String format(double[] d, NumberFormat nf) { - return format(d, " ", nf); + public static String format(double[] d, String sep, NumberFormat nf) { + if(d.length == 0) { + return ""; + } + return formatTo(new StringBuilder(), d, sep, nf).toString(); } /** @@ -218,42 +209,49 @@ public final class FormatUtil { * @param d the double array to be formatted * @param sep the separator between the single values of the double array, * e.g. ',' - * @param nf the number format to be used for formatting * @return a String representing the double array d */ - public static String format(double[] d, String sep, NumberFormat nf) { - StringBuilder buffer = new StringBuilder(); - for(int i = 0; i < d.length; i++) { - if(i < d.length - 1) { - buffer.append(format(d[i], nf)).append(sep); - } - else { - buffer.append(format(d[i], nf)); - } + public static StringBuilder formatTo(StringBuilder a, double[] d, String sep) { + if(d.length == 0) { + return a; } - return buffer.toString(); + a.append(d[0]); + for(int i = 1; i < d.length; i++) { + a.append(sep); + a.append(d[i]); + } + return a; } /** - * Formats the double array d with ',' as separator and 2 fraction digits. + * Formats the double array d with the specified number format. * * @param d the double array to be formatted + * @param sep the separator between the single values of the double array, + * e.g. ',' + * @param nf the number format to be used for formatting * @return a String representing the double array d */ - public static String format(double[] d) { - return format(d, ", ", 2); + public static StringBuilder formatTo(StringBuilder a, double[] d, String sep, NumberFormat nf) { + if(d.length == 0) { + return a; + } + a.append(nf.format(d[0])); + for(int i = 1; i < d.length; i++) { + a.append(sep); + a.append(nf.format(d[i])); + } + return a; } /** - * Formats the double array d with ', ' as separator and with the specified - * fraction digits. + * Formats the double array d with ', ' as separator and 2 fraction digits. * * @param d the double array to be formatted - * @param digits the number of fraction digits * @return a String representing the double array d */ - public static String format(double[] d, int digits) { - return format(d, ", ", digits); + public static String format(double[] d) { + return formatTo(new StringBuilder(), d, ", ").toString(); } /** @@ -263,11 +261,7 @@ public final class FormatUtil { * @return a String representing the double array d */ public static String format(double[][] d) { - StringBuilder buffer = new StringBuilder(); - for(double[] array : d) { - buffer.append(format(array, ", ", 2)).append('\n'); - } - return buffer.toString(); + return format(d, "\n", ", ", NF2); } /** @@ -277,110 +271,40 @@ public final class FormatUtil { * @param d the double array to be formatted * @param sep1 the first separator of the outer array * @param sep2 the second separator of the inner array - * @param digits the number of fraction digits + * @param nf the number format to use * @return a String representing the double array d */ - public static String format(double[][] d, String sep1, String sep2, int digits) { - StringBuilder buffer = new StringBuilder(); - - for(int i = 0; i < d.length; i++) { - if(i < d.length - 1) { - buffer.append(format(d[i], sep2, digits)).append(sep1); - } - else { - buffer.append(format(d[i], sep2, digits)); - } + public static String format(double[][] d, String sep1, String sep2, NumberFormat nf) { + if(d.length == 0) { + return ""; } - - return buffer.toString(); - } - - /** - * Formats the Double array f with the specified separator and the specified - * fraction digits. - * - * @param f the Double array to be formatted - * @param sep the separator between the single values of the Double array, - * e.g. ',' - * @param digits the number of fraction digits - * @return a String representing the Double array f - */ - public static String format(Double[] f, String sep, int digits) { StringBuilder buffer = new StringBuilder(); - for(int i = 0; i < f.length; i++) { - if(i < f.length - 1) { - buffer.append(format(f[i].doubleValue(), digits)).append(sep); - } - else { - buffer.append(format(f[i].doubleValue(), digits)); - } + formatTo(buffer, d[0], sep2, nf); + for(int i = 1; i < d.length; i++) { + buffer.append(sep1); + formatTo(buffer, d[i], sep2, nf); } return buffer.toString(); } /** - * Formats the Double array f with ',' as separator and 2 fraction digits. - * - * @param f the Double array to be formatted - * @return a String representing the Double array f - */ - public static String format(Double[] f) { - return format(f, ", ", 2); - } - - /** - * Formats the Double array f with the specified separator and the specified - * fraction digits. + * Formats the double array d with the specified number format. * - * @param f the Double array to be formatted - * @param sep the separator between the single values of the Double array, + * @param d the double array to be formatted + * @param sep the separator between the single values of the double array, * e.g. ',' - * @param nf the number format - * @return a String representing the Double array f + * @param nf the number format to be used for formatting + * @return a String representing the double array d */ - public static String format(Double[] f, String sep, NumberFormat nf) { - StringBuilder buffer = new StringBuilder(); - for(int i = 0; i < f.length; i++) { - if(i < f.length - 1) { - buffer.append(format(f[i].doubleValue(), nf)).append(sep); - } - else { - buffer.append(format(f[i].doubleValue(), nf)); - } + public static String format(float[] d, String sep, NumberFormat nf) { + if(d.length == 0) { + return ""; } - return buffer.toString(); - } - - /** - * Formats the Double array f with ',' as separator and 2 fraction digits. - * - * @param f the Double array to be formatted - * @param nf the Number format - * @return a String representing the Double array f - */ - public static String format(Double[] f, NumberFormat nf) { - return format(f, ", ", nf); - } - - /** - * Formats the float array f with the specified separator and the specified - * fraction digits. - * - * @param f the float array to be formatted - * @param sep the separator between the single values of the float array, e.g. - * ',' - * @param digits the number of fraction digits - * @return a String representing the float array f - */ - public static String format(float[] f, String sep, int digits) { StringBuilder buffer = new StringBuilder(); - for(int i = 0; i < f.length; i++) { - if(i < f.length - 1) { - buffer.append(format(f[i], digits)).append(sep); - } - else { - buffer.append(format(f[i], digits)); - } + buffer.append(nf.format((double) d[0])); + for(int i = 1; i < d.length; i++) { + buffer.append(sep); + buffer.append(nf.format((double) d[i])); } return buffer.toString(); } @@ -392,7 +316,7 @@ public final class FormatUtil { * @return a String representing the float array f */ public static String format(float[] f) { - return format(f, ", ", 2); + return format(f, ", ", NF2); } /** @@ -404,14 +328,14 @@ public final class FormatUtil { * @return a String representing the int array a */ public static String format(int[] a, String sep) { + if(a.length == 0) { + return ""; + } StringBuilder buffer = new StringBuilder(); - for(int i = 0; i < a.length; i++) { - if(i < a.length - 1) { - buffer.append(a[i]).append(sep); - } - else { - buffer.append(a[i]); - } + buffer.append(a[0]); + for(int i = 1; i < a.length; i++) { + buffer.append(sep); + buffer.append(a[i]); } return buffer.toString(); } @@ -427,51 +351,20 @@ public final class FormatUtil { } /** - * Formats the Integer array a for printing purposes. - * - * @param a the Integer array to be formatted - * @param sep the separator between the single values of the float array, e.g. - * ',' - * @return a String representing the Integer array a - */ - public static String format(Integer[] a, String sep) { - StringBuilder buffer = new StringBuilder(); - for(int i = 0; i < a.length; i++) { - if(i < a.length - 1) { - buffer.append(a[i]).append(sep); - } - else { - buffer.append(a[i]); - } - } - return buffer.toString(); - } - - /** - * Formats the Integer array a for printing purposes. - * - * @param a the Integer array to be formatted - * @return a String representing the Integer array a - */ - public static String format(Integer[] a) { - return format(a, ", "); - } - - /** * Formats the long array a for printing purposes. * * @param a the long array to be formatted * @return a String representing the long array a */ public static String format(long[] a) { + if(a.length == 0) { + return ""; + } StringBuilder buffer = new StringBuilder(); - for(int i = 0; i < a.length; i++) { - if(i < a.length - 1) { - buffer.append(a[i]).append(", "); - } - else { - buffer.append(a[i]); - } + buffer.append(a[0]); + for(int i = 1; i < a.length; i++) { + buffer.append(", "); + buffer.append(a[i]); } return buffer.toString(); } @@ -483,14 +376,14 @@ public final class FormatUtil { * @return a String representing the byte array a */ public static String format(byte[] a) { + if(a.length == 0) { + return ""; + } StringBuilder buffer = new StringBuilder(); - for(int i = 0; i < a.length; i++) { - if(i < a.length - 1) { - buffer.append(a[i]).append(", "); - } - else { - buffer.append(a[i]); - } + buffer.append(a[0]); + for(int i = 1; i < a.length; i++) { + buffer.append(", "); + buffer.append(a[i]); } return buffer.toString(); } @@ -504,14 +397,14 @@ public final class FormatUtil { * @return a String representing the boolean array b */ public static String format(boolean[] b, final String sep) { + if(b.length == 0) { + return ""; + } StringBuilder buffer = new StringBuilder(); - for(int i = 0; i < b.length; i++) { - if(i < b.length - 1) { - buffer.append(format(b[i])).append(sep); - } - else { - buffer.append(format(b[i])); - } + buffer.append(format(b[0])); + for(int i = 1; i < b.length; i++) { + buffer.append(sep); + buffer.append(format(b[i])); } return buffer.toString(); } @@ -523,10 +416,7 @@ public final class FormatUtil { * @return a String representing of the boolean b */ public static String format(final boolean b) { - if(b) { - return "1"; - } - return "0"; + return b ? "1" : "0"; } /** @@ -539,19 +429,11 @@ public final class FormatUtil { */ public static String format(BitSet bitSet, int dim, String sep) { StringBuilder msg = new StringBuilder(); - - for(int d = 0; d < dim; d++) { - if(d > 0) { - msg.append(sep); - } - if(bitSet.get(d)) { - msg.append('1'); - } - else { - msg.append('0'); - } + msg.append(bitSet.get(0) ? '1' : '0'); + for(int d = 1; d < dim; d++) { + msg.append(sep); + msg.append(bitSet.get(d) ? '1' : '0'); } - return msg.toString(); } @@ -583,13 +465,11 @@ public final class FormatUtil { return d.iterator().next(); } StringBuilder buffer = new StringBuilder(); - boolean first = true; - for(String str : d) { - if(!first) { - buffer.append(sep); - } - buffer.append(str); - first = false; + Iterator<String> it = d.iterator(); + buffer.append(it.next()); + while(it.hasNext()) { + buffer.append(sep); + buffer.append(it.next()); } return buffer.toString(); } @@ -745,7 +625,11 @@ public final class FormatUtil { * given NumberFormat */ public static String format(Vector m, NumberFormat nf) { - return "[" + FormatUtil.format(m.getArrayRef(), nf) + "]"; + StringBuilder buf = new StringBuilder(); + buf.append('['); + formatTo(buf, m.getArrayRef(), ", ", nf); + buf.append(']'); + return buf.toString(); } /** @@ -754,7 +638,7 @@ public final class FormatUtil { * @return String representation of this Vector */ public static String format(Vector m) { - return format(m, FormatUtil.NF); + return format(m.getArrayRef()); } /** @@ -1025,6 +909,18 @@ public final class FormatUtil { /** * Preallocated exceptions. */ + private static final NumberFormatException EMPTY_STRING = new NumberFormatException("Parser called on an empty string.") { + private static final long serialVersionUID = 1L; + + @Override + public synchronized Throwable fillInStackTrace() { + return this; + } + }; + + /** + * Preallocated exceptions. + */ private static final NumberFormatException EXPONENT_OVERFLOW = new NumberFormatException("Precision overflow for double exponent.") { private static final long serialVersionUID = 1L; @@ -1111,6 +1007,9 @@ public final class FormatUtil { * @return Double value */ public static double parseDouble(final CharSequence str, final int start, final int end) { + if(start >= end) { + throw EMPTY_STRING; + } // Current position and character. int pos = start; char cur = str.charAt(pos); @@ -1165,7 +1064,7 @@ public final class FormatUtil { // Reads exponent. int exp = 0; - if((pos < end) && ((cur == 'E') || (cur == 'e'))) { + if((pos + 1 < end) && ((cur == 'E') || (cur == 'e'))) { cur = str.charAt(++pos); final boolean isNegativeExp = (cur == '-'); if((isNegativeExp || (cur == '+')) && (++pos < end)) { @@ -1211,6 +1110,122 @@ public final class FormatUtil { } /** + * Parse a double from a character sequence. + * + * In contrast to Javas {@link Double#parseDouble}, this will <em>not</em> + * create an object and thus is expected to put less load on the garbage + * collector. It will accept some more spellings of NaN and infinity, thus + * removing the need for checking for these independently. + * + * @param str String + * @param start Begin + * @param end End + * @return Double value + */ + public static double parseDouble(final byte[] str, final int start, final int end) { + if(start >= end) { + throw EMPTY_STRING; + } + // Current position and character. + int pos = start; + byte cur = str[pos]; + + // Match for NaN spellings + if(matchNaN(str, cur, pos, end)) { + return Double.NaN; + } + // Match sign + boolean isNegative = (cur == '-'); + // Carefully consume the - character, update c and i: + if((isNegative || (cur == '+')) && (++pos < end)) { + cur = str[pos]; + } + if(matchInf(str, cur, pos, end)) { + return isNegative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; + } + + // Begin parsing real numbers! + if(((cur < '0') || (cur > '9')) && (cur != '.')) { + throw NOT_A_NUMBER; + } + + // Parse digits into a long, remember offset of decimal point. + long decimal = 0; + int decimalPoint = -1; + while(true) { + final int digit = cur - '0'; + if((digit >= 0) && (digit <= 9)) { + final long tmp = (decimal << 3) + (decimal << 1) + digit; + if((decimal > MAX_LONG_OVERFLOW) || (tmp < decimal)) { + throw PRECISION_OVERFLOW; + } + decimal = tmp; + } + else if((cur == '.') && (decimalPoint < 0)) { + decimalPoint = pos; + } + else { // No more digits, or a second dot. + break; + } + if(++pos < end) { + cur = str[pos]; + } + else { + break; + } + } + // We need the offset from the back for adjusting the exponent: + // Note that we need the current value of i! + decimalPoint = (decimalPoint >= 0) ? pos - decimalPoint - 1 : 0; + + // Reads exponent. + int exp = 0; + if((pos + 1 < end) && ((cur == 'E') || (cur == 'e'))) { + cur = str[++pos]; + final boolean isNegativeExp = (cur == '-'); + if((isNegativeExp || (cur == '+')) && (++pos < end)) { + cur = str[pos]; + } + if((cur < '0') || (cur > '9')) { // At least one digit required. + throw INVALID_EXPONENT; + } + while(true) { + final int digit = cur - '0'; + if((digit >= 0) && (digit < 10)) { + final int tmp = (exp << 3) + (exp << 1) + digit; + // Actually, double can only handle Double.MAX_EXPONENT? How about + // subnormal? + if((exp > MAX_INT_OVERFLOW) || (tmp < exp)) { + throw EXPONENT_OVERFLOW; + } + exp = tmp; + } + else { + break; + } + if(++pos < end) { + cur = str[pos]; + } + else { + break; + } + } + if(isNegativeExp) { + exp = -exp; + } + } + // Adjust exponent by the offset of the dot in our long. + if(decimalPoint >= 0) { + exp = exp - decimalPoint; + } + if(pos != end) { + throw TRAILING_CHARACTERS; + } + + return BitsUtil.lpow10(isNegative ? -decimal : decimal, exp); + } + + /** * Match "NaN" in a number of different capitalizations. * * @param str String to match @@ -1243,6 +1258,38 @@ public final class FormatUtil { } /** + * Match "NaN" in a number of different capitalizations. + * + * @param str String to match + * @param firstchar First character + * @param start Interval begin + * @param end Interval end + * @return {@code true} when NaN was recognized. + */ + private static boolean matchNaN(byte[] str, byte firstchar, int start, int end) { + final int len = end - start; + if(len < 2 || len > 3) { + return false; + } + if(firstchar != 'N' && firstchar != 'n') { + return false; + } + final byte c1 = str[start + 1]; + if(c1 != 'a' && c1 != 'A') { + return false; + } + // Accept just "NA", too: + if(len == 2) { + return true; + } + final byte c2 = str[start + 2]; + if(c2 != 'N' && c2 != 'n') { + return false; + } + return true; + } + + /** * Maximum long that we can process without overflowing. */ private static final long MAX_LONG_OVERFLOW = Long.MAX_VALUE / 10; @@ -1297,6 +1344,40 @@ public final class FormatUtil { } /** + * Match "inf", "infinity" in a number of different capitalizations. + * + * @param str String to match + * @param firstchar First character + * @param start Interval begin + * @param end Interval end + * @return {@code true} when infinity was recognized. + */ + private static boolean matchInf(byte[] str, byte firstchar, int start, int end) { + final int len = end - start; + // The wonders of unicode. This is more than one byte on UTF-8 + if(len == 1 && firstchar == '∞') { + return true; + } + if(len != 3 && len != INFINITY_LENGTH) { + return false; + } + // Test beginning: "inf" + if(firstchar != 'I' && firstchar != 'i') { + return false; + } + for(int i = 1, j = INFINITY_LENGTH + 1; i < INFINITY_LENGTH; i++, j++) { + final byte c = str[start + i]; + if(c != INFINITY_PATTERN[i] && c != INFINITY_PATTERN[j]) { + return false; + } + if(i == 2 && len == 3) { + return true; + } + } + return true; + } + + /** * Parse a long integer from a character sequence. * * @param str String @@ -1348,4 +1429,143 @@ public final class FormatUtil { return isNegative ? -decimal : decimal; } + + /** + * Format a boolean value as string "true" or "false". + * + * @param b Boolean to Format + * @param buf Buffer to append to + * @return Same buffer + */ + public static StringBuilder format(boolean b, StringBuilder buf) { + return buf.append(b ? "true" : "false"); + } + + /** + * Format a boolean value as string "1" or "0". + * + * @param b Boolean to Format + * @param buf Buffer to append to + * @return Same buffer + */ + public static StringBuilder formatBit(boolean b, StringBuilder buf) { + return buf.append(b ? '1' : '0'); + } + + /** + * Format an integer value as decimal. + * + * @param i Integer value to format. + * @param buf Buffer to append to + * @return Same buffer + */ + public static StringBuilder format(int i, StringBuilder buf) { + // Int seems to be well optimized + return buf.append(i); + } + + /** + * Format a long value as decimal. + * + * @param i Long value to format. + * @param buf Buffer to append to + * @return Same buffer + */ + public static StringBuilder format(long i, StringBuilder buf) { + // Long seems to be well optimized + return buf.append(i); + } + + /** + * Buffer for zero padding. + */ + private static final char[] ZEROPADDING = new char[] { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0' }; + + /** + * Buffer for whitespace padding. + */ + private static final char[] SPACEPADDING = new char[] { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; + + /** + * Append zeros to a buffer. + * + * @param buf Buffer to append to + * @param zeros Number of zeros to append. + * @return Buffer + */ + public static StringBuilder appendZeros(StringBuilder buf, int zeros) { + for(int i = zeros; i > 0; i -= ZEROPADDING.length) { + buf.append(ZEROPADDING, 0, i < ZEROPADDING.length ? i : ZEROPADDING.length); + } + return buf; + } + + /** + * Append whitespace to a buffer. + * + * @param buf Buffer to append to + * @param spaces Number of spaces to append. + * @return Buffer + */ + public static StringBuilder appendSpace(StringBuilder buf, int spaces) { + for(int i = spaces; i > 0; i -= SPACEPADDING.length) { + buf.append(SPACEPADDING, 0, i < SPACEPADDING.length ? i : SPACEPADDING.length); + } + return buf; + } + + /** + * Compute the number of characters needed to represent the integer x. + * + * Reimplementation of {@link Long#stringSize}, but public and without loop. + * + * @param x Integer value + * @return Number of digits needed + */ + public static int stringSize(int x) { + if(x < 0) { + // Avoid overflow on extreme negative + return (x == Integer.MIN_VALUE) ? 11 : stringSize(-x) + 1; + } + // This is almost a binary search - 10 cases is not a power of two, and we + // assume that the smaller values are more frequent. + return // + (x < 10000) ? // 1-4 vs. 5-10 + /**/(x < 100) ? // 1-2 vs. 3-4 + /* */((x < 10) ? 1 : 2) : // + /* */((x < 1000) ? 3 : 4) : // + /**/(x < 1000000) ? // 5-6 vs. 7-10 + /* */((x < 100000) ? 5 : 6) : // 5-6 + /* */(x < 100000000) ? // 7-8 vs. 9-10 + /* */((x < 10000000) ? 7 : 8) : // 7-8 + /* */((x < 1000000000) ? 9 : 10) // 9-10 + ; + } + + /** + * Compute the number of characters needed to represent the integer x. + * + * Reimplementation of {@link Long#stringSize}, but public and without loop. + * + * @param x Integer value + * @return Number of digits needed + */ + public static int stringSize(long x) { + if(x < 0) { + // Avoid overflow on extreme negative + return (x == Long.MIN_VALUE) ? 20 : stringSize(-x) + 1; + } + // This is almost a binary search. + return (x <= Integer.MAX_VALUE) ? stringSize((int) x) : // + (x < 10000000000000L) ? // 10-13 vs. 14-19 + /**/(x < 100000000000L) ? // 10-11 vs. 12-13 + /* */((x < 10000000000L) ? 10 : 11) : // + /* */((x < 1000000000000L) ? 12 : 13) : // + /**/(x < 1000000000000000L) ? // 14-15 vs. 16-19 + /* */((x < 100000000000000L) ? 14 : 15) : // 14-15 + /* */(x < 100000000000000000L) ? // 16-17 vs. 18-19 + /* */((x < 10000000000000000L) ? 16 : 17) : // 16-17 + /* */((x < 1000000000000000000L) ? 18 : 19) // 18-19 + ; + } } |