/*
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
* Copyright (C) 2005 - Javolution (http://javolution.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package javolution37.javolution.lang;
import java.lang.CharSequence;
import javolution37.javolution.lang.TextFormat.Cursor;
import java.io.IOException;
/**
* This class provides utility methods to parse CharSequence
* into primitive types and to format primitive types into an
* Appendable.
*
* Methods from this class do not create temporary objects
* and are typically faster than standard library methods (e.g
* TypeFormat#parseDouble is up to 15x faster than
* Double.parseDouble).
*
* For non-primitive objects, formatting is typically performed using
* specialized {@link TextFormat} instances.
*
* @author Jean-Marie Dautelle
* @version 3.7, January 1, 2006
*/
public final class TypeFormat {
/**
* Holds the digits to character mapping.
*/
private final static char[] DIGIT_TO_CHAR = { '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z' };
/**
* Holds the character to digit mapping.
*/
private final static int[] CHAR_TO_DIGIT = new int[128];
static {
for (int i = 0; i < CHAR_TO_DIGIT.length; i++) {
CHAR_TO_DIGIT[i] = -1;
}
for (int i = 0; i < DIGIT_TO_CHAR.length; i++) {
CHAR_TO_DIGIT[DIGIT_TO_CHAR[i]] = i;
CHAR_TO_DIGIT[Character.toUpperCase(DIGIT_TO_CHAR[i])] = i;
}
}
/**
* Default constructor (forbids derivation).
*/
private TypeFormat() {
}
/**
* Parses the specified character sequence as a boolean.
*
* @param csq the character sequence to parse.
* @return the corresponding boolean value.
* @throws IllegalArgumentException if the specified character sequence
* is different from "true" or "false" ignoring cases.
*/
public static boolean parseBoolean(CharSequence csq) {
if ((csq.length() == 4)
&& (csq.charAt(0) == 't' || csq.charAt(0) == 'T')
&& (csq.charAt(1) == 'r' || csq.charAt(1) == 'R')
&& (csq.charAt(2) == 'u' || csq.charAt(2) == 'U')
&& (csq.charAt(3) == 'e' || csq.charAt(3) == 'E')) {
return true;
} else if ((csq.length() == 5)
&& (csq.charAt(0) == 'f' || csq.charAt(0) == 'F')
&& (csq.charAt(1) == 'a' || csq.charAt(1) == 'A')
&& (csq.charAt(2) == 'l' || csq.charAt(2) == 'L')
&& (csq.charAt(3) == 's' || csq.charAt(3) == 'S')
&& (csq.charAt(4) == 'e' || csq.charAt(4) == 'E')) {
return false;
}
throw new IllegalArgumentException("Cannot parse " + csq
+ " as boolean");
}
/**
* Parses the specified character sequence from the specified position
* as a boolean.
*
* @param csq the character sequence to parse.
* @param cursor the current cursor position (being maintained).
* @return the next boolean value.
* @throws IllegalArgumentException if the character sequence from the
* specified position is different from "true" or "false" ignoring
* cases.
*/
public static boolean parseBoolean(CharSequence csq, Cursor cursor) {
final int i = cursor.getIndex();
if ((csq.length() > i + 4)
&& (csq.charAt(i) == 't' || csq.charAt(i) == 'T')
&& (csq.charAt(i + 1) == 'r' || csq.charAt(i + 1) == 'R')
&& (csq.charAt(i + 2) == 'u' || csq.charAt(i + 2) == 'U')
&& (csq.charAt(i + 3) == 'e' || csq.charAt(i + 3) == 'E')) {
cursor.increment(4);
return true;
}
if ((csq.length() > i + 5)
&& (csq.charAt(i) == 'f' || csq.charAt(i) == 'F')
&& (csq.charAt(i + 1) == 'a' || csq.charAt(i + 1) == 'A')
&& (csq.charAt(i + 2) == 'l' || csq.charAt(i + 2) == 'L')
&& (csq.charAt(i + 3) == 's' || csq.charAt(i + 3) == 'S')
&& (csq.charAt(i + 4) == 'e' || csq.charAt(i + 4) == 'E')) {
cursor.increment(5);
return true;
}
throw new IllegalArgumentException("Cannot parse boolean at "
+ cursor.getIndex());
}
/**
* Parses the specified character sequence as a signed decimal
* byte.
*
* @param csq the character sequence to parse.
* @return parseByte(csq, 10)
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable byte.
* @see #parseByte(CharSequence, int)
*/
public static byte parseByte(CharSequence csq) {
return parseByte(csq, 10);
}
/**
* Parses the specified character sequence as a signed byte
* in the specified radix.
*
* @param csq the character sequence to parse.
* @param radix the radix to be used while parsing.
* @return the corresponding byte.
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable byte.
*/
public static byte parseByte(CharSequence csq, int radix) {
int i = parseInt(csq, radix);
if ((i < Byte.MIN_VALUE) || (i > Byte.MAX_VALUE))
throw new NumberFormatException("Overflow");
return (byte) i;
}
/**
* Parses the specified character sequence from the specified position
* as a signed byte in the specified radix.
*
* @param csq the character sequence to parse.
* @param radix the radix to be used while parsing.
* @param cursor the current cursor position (being maintained).
* @return the corresponding byte.
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable byte.
*/
public static byte parseByte(CharSequence csq, int radix, Cursor cursor) {
int i = parseInt(csq, radix, cursor);
if ((i < Byte.MIN_VALUE) || (i > Byte.MAX_VALUE))
throw new NumberFormatException("Overflow");
return (byte) i;
}
/**
* Parses the specified character sequence as a signed decimal
* short.
*
* @param csq the character sequence to parse.
* @return parseShort(csq, 10)
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable short.
* @see #parseShort(CharSequence, int)
*/
public static short parseShort(CharSequence csq) {
return parseShort(csq, 10);
}
/**
* Parses the specified character sequence as a signed short
* in the specified radix.
*
* @param csq the character sequence to parse.
* @param radix the radix to be used while parsing.
* @return the corresponding short.
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable short.
*/
public static short parseShort(CharSequence csq, int radix) {
int i = parseInt(csq, radix);
if ((i < Short.MIN_VALUE) || (i > Short.MAX_VALUE))
throw new NumberFormatException("Overflow");
return (short) i;
}
/**
* Parses the specified character sequence from the specified position
* as a signed short in the specified radix.
*
* @param csq the character sequence to parse.
* @param radix the radix to be used while parsing.
* @param cursor the current cursor position (being maintained).
* @return the corresponding short.
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable short.
*/
public static short parseShort(CharSequence csq, int radix, Cursor cursor) {
int i = parseInt(csq, radix, cursor);
if ((i < Short.MIN_VALUE) || (i > Short.MAX_VALUE))
throw new NumberFormatException("Overflow");
return (short) i;
}
/**
* Parses the specified character sequence as a signed int.
*
* @param csq the character sequence to parse.
* @return parseInt(csq, 10)
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable int.
* @see #parseInt(CharSequence, int)
*/
public static int parseInt(CharSequence csq) {
return parseInt(csq, 10);
}
/**
* Parses the specified character sequence as a signed int
* in the specified radix.
*
* @param csq the character sequence to parse.
* @param radix the radix to be used while parsing.
* @return the corresponding int.
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable int.
*/
public static int parseInt(CharSequence csq, int radix) {
try {
int length = csq.length();
int i = 0;
boolean isNegative = (csq.charAt(i) == '-') ? true : false;
i += (isNegative || (csq.charAt(i) == '+')) ? 1 : 0;
char c0 = csq.charAt(i);
int digit = (c0 < CHAR_TO_DIGIT.length) ? CHAR_TO_DIGIT[c0] : -1;
if ((digit < 0) || (digit >= radix))
throw new NumberFormatException("Digit expected");
int result = -digit; // Accumulates negatively.
while (true) {
if (++i >= length)
break;
final char c = csq.charAt(i);
digit = (c < CHAR_TO_DIGIT.length) ? CHAR_TO_DIGIT[c] : -1;
if ((digit < 0) || (digit >= radix))
break;
if (result <= Integer.MIN_VALUE / 36) { // Checks for potential overflow.
if (result < (Integer.MIN_VALUE + digit) / radix)
throw new NumberFormatException("Overflow");
}
result = result * radix - digit;
}
if (!isNegative && (result == Integer.MIN_VALUE)) // Negation overflow.
throw new NumberFormatException("Overflow");
return isNegative ? result : -result;
} catch (IndexOutOfBoundsException e) {
throw new NumberFormatException();
}
}
/**
* Parses the specified character sequence from the specified position
* as a signed int in the specified radix.
*
* @param csq the character sequence to parse.
* @param radix the radix to be used while parsing.
* @param cursor the current cursor position (being maintained).
* @return the corresponding int.
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable int.
*/
public static int parseInt(CharSequence csq, int radix, Cursor cursor) {
try {
int length = csq.length();
int i = cursor.getIndex();
boolean isNegative = (csq.charAt(i) == '-') ? true : false;
i += (isNegative || (csq.charAt(i) == '+')) ? 1 : 0;
char c0 = csq.charAt(i);
int digit = (c0 < CHAR_TO_DIGIT.length) ? CHAR_TO_DIGIT[c0] : -1;
if ((digit < 0) || (digit >= radix))
throw new NumberFormatException("Digit expected");
int result = -digit; // Accumulates negatively.
while (true) {
if (++i >= length)
break;
final char c = csq.charAt(i);
digit = (c < CHAR_TO_DIGIT.length) ? CHAR_TO_DIGIT[c] : -1;
if ((digit < 0) || (digit >= radix))
break;
if (result <= Integer.MIN_VALUE / 36) { // Checks for potential overflow.
if (result < (Integer.MIN_VALUE + digit) / radix)
throw new NumberFormatException("Overflow");
}
result = result * radix - digit;
}
if (!isNegative && (result == Integer.MIN_VALUE)) // Negation overflow.
throw new NumberFormatException("Overflow");
cursor.setIndex(i);
return isNegative ? result : -result;
} catch (IndexOutOfBoundsException e) {
throw new NumberFormatException();
}
}
/**
* Parses the specified character sequence as a decimal long.
*
* @param csq the character sequence to parse.
* @return parseLong(csq, 10)
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable long.
* @see #parseLong(CharSequence, int)
*/
public static long parseLong(CharSequence csq) {
return parseLong(csq, 10);
}
/**
* Parses the specified character sequence as a signed long
* in the specified radix.
*
* @param csq the character sequence to parse.
* @param radix the radix to be used while parsing.
* @return the corresponding long.
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable long.
*/
public static long parseLong(CharSequence csq, int radix) {
try {
int length = csq.length();
int i = 0;
boolean isNegative = (csq.charAt(i) == '-') ? true : false;
i += (isNegative || (csq.charAt(i) == '+')) ? 1 : 0;
char c0 = csq.charAt(i);
int digit = (c0 < CHAR_TO_DIGIT.length) ? CHAR_TO_DIGIT[c0] : -1;
if ((digit < 0) || (digit >= radix))
throw new NumberFormatException("Digit expected");
long result = -digit; // Accumulates negatively.
while (true) {
if (++i >= length)
break;
final char c = csq.charAt(i);
digit = (c < CHAR_TO_DIGIT.length) ? CHAR_TO_DIGIT[c] : -1;
if ((digit < 0) || (digit >= radix))
break;
if (result <= Long.MIN_VALUE / 36) { // Checks for potential overflow.
if (result < (Long.MIN_VALUE + digit) / radix)
throw new NumberFormatException("Overflow");
}
result = result * radix - digit;
}
if (!isNegative && (result == Long.MIN_VALUE)) // Negation overflow.
throw new NumberFormatException("Overflow");
return isNegative ? result : -result;
} catch (IndexOutOfBoundsException e) {
throw new NumberFormatException();
}
}
/**
* Parses the specified character sequence from the specified position
* as a signed long in the specified radix.
*
* @param csq the character sequence to parse.
* @param radix the radix to be used while parsing.
* @param cursor the current cursor position (being maintained).
* @return the corresponding long.
* @throws NumberFormatException if the specified character sequence
* does not contain a parsable long.
*/
public static long parseLong(CharSequence csq, int radix, Cursor cursor) {
try {
int length = csq.length();
int i = cursor.getIndex();
boolean isNegative = (csq.charAt(i) == '-') ? true : false;
i += (isNegative || (csq.charAt(i) == '+')) ? 1 : 0;
char c0 = csq.charAt(i);
int digit = (c0 < CHAR_TO_DIGIT.length) ? CHAR_TO_DIGIT[c0] : -1;
if ((digit < 0) || (digit >= radix))
throw new NumberFormatException("Digit expected");
long result = -digit; // Accumulates negatively.
while (true) {
if (++i >= length)
break;
final char c = csq.charAt(i);
digit = (c < CHAR_TO_DIGIT.length) ? CHAR_TO_DIGIT[c] : -1;
if ((digit < 0) || (digit >= radix))
break;
if (result <= Long.MIN_VALUE / 36) { // Checks for potential overflow.
if (result < (Long.MIN_VALUE + digit) / radix)
throw new NumberFormatException("Overflow");
}
result = result * radix - digit;
}
if (!isNegative && (result == Long.MIN_VALUE)) // Negation overflow.
throw new NumberFormatException("Overflow");
cursor.setIndex(i);
return isNegative ? result : -result;
} catch (IndexOutOfBoundsException e) {
throw new NumberFormatException();
}
}
/**
* Parses the specified character sequence as a float.
*
* @param csq the character sequence to parse.
* @return the float number represented by the specified character sequence.
* @throws NumberFormatException if the character sequence does not contain
* a parsable float.
/**/
public static float parseFloat(CharSequence csq) {
double d = parseDouble(csq);
if ((d >= -Float.MAX_VALUE) && (d <= Float.MAX_VALUE)) {
return (float) d;
} else {
throw new NumberFormatException("Overflow");
}
}
/**/
/**
* Parses the specified character sequence from the specified position
* as a float.
*
* @param csq the character sequence to parse.
* @param cursor the current cursor position (being maintained).
* @return the float number represented by the specified character sequence.
* @throws NumberFormatException if the character sequence does not contain
* a parsable float.
/**/
public static float parseFloat(CharSequence csq, Cursor cursor) {
double d = parseDouble(csq, cursor);
if ((d >= -Float.MAX_VALUE) && (d <= Float.MAX_VALUE)) {
return (float) d;
} else {
throw new NumberFormatException("Overflow");
}
}
/**/
/**
* Parses the specified character sequence as a double.
*
* @param csq the character sequence to parse.
* @return the double number represented by this character sequence.
* @throws NumberFormatException if the character sequence does not contain
* a parsable double.
/**/
public static double parseDouble(CharSequence csq)
throws NumberFormatException {
Cursor cursor = Cursor.newInstance();
double result = TypeFormat.parseDouble(csq, cursor);
cursor.recycle();
return result;
}
/**/
/**
* Parses the specified character sequence from the specified position
* as a double.
*
* @param csq the character sequence to parse.
* @param cursor the current cursor position (being maintained).
* @return the double number represented by this character sequence.
* @throws NumberFormatException if the character sequence does not contain
* a parsable double.
/**/
public static double parseDouble(CharSequence csq, Cursor cursor)
throws NumberFormatException {
try {
int i = cursor.getIndex();
// Reads the sign now (which might not be the sign of the decimal part, e.g. -0.XXX).
boolean isNegative = (csq.charAt(i) == '-') ? true : false;
i += (isNegative || (csq.charAt(i) == '+')) ? 1 : 0;
char c0 = csq.charAt(i);
if ((c0 == 'N') && startWith(csq, i, "NaN")) {
cursor.setIndex(i + 3);
return Double.NaN;
}
if ((c0 == 'I') && startWith(csq, i, "Infinity")) {
cursor.setIndex(i + 8);
return isNegative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
}
cursor.setIndex(i);
long decimal = parseLong(csq, 10, cursor);
final int length = csq.length();
// Reads fraction.
long fraction = 0;
int fractionLength = 0;
if (cursor.getIndex() < length) {
char c = csq.charAt(cursor.getIndex());
if (c == '.') {
cursor.increment();
int startFraction = cursor.getIndex();
fraction = TypeFormat.parseLong(csq, 10, cursor);
fractionLength = cursor.getIndex() - startFraction;
}
}
// Reads exponent.
int exponent = 0;
if (cursor.getIndex() < length) {
char c = csq.charAt(cursor.getIndex());
if ((c == 'e') || (c == 'E')) {
cursor.increment();
exponent = TypeFormat.parseInt(csq, 10, cursor);
}
}
// Adds decimal and fraction part.
double decimalAsDouble = MathLib.toDouble(decimal, exponent);
double fractionAsDouble = MathLib.toDouble(
decimal < 0 ? -fraction : fraction, exponent - fractionLength);
return isNegative ? - decimalAsDouble - fractionAsDouble :
decimalAsDouble + fractionAsDouble;
} catch (IndexOutOfBoundsException e) {
throw new NumberFormatException();
}
}
/**/
static boolean startWith(CharSequence csq, int start, String str) {
for (int i = 0; i < str.length(); i++) {
if ((start + i >= csq.length())
|| csq.charAt(start + i) != str.charAt(i))
return false;
}
return true;
}
/**
* Formats the specified boolean and appends the resulting
* text to the Appendable argument.
*
* @param b a boolean.
* @param csq the Appendable to append.
* @return the specified StringBuffer object.
* @throws IOException if an I/O exception occurs.
* @see #parseBoolean
*/
public static Appendable format(boolean b, Appendable csq)
throws IOException {
return b ? append(csq, "true") : append(csq, "false");
}
/**
* Formats the specified int and appends the resulting
* text (decimal representation) to the Appendable argument.
*
* Note: This method is preferred to Appendable.append(int)
* as it does not create temporary String
* objects (several times faster for small numbers).
*
* @param i the int number.
* @param csq the Appendable to append.
* @return the specified Appendable object.
* @throws IOException if an I/O exception occurs.
* @see #parseInt
*/
public static Appendable format(int i, Appendable csq) throws IOException {
if (i <= 0) {
if (i == Integer.MIN_VALUE) { // Negation would overflow.
return append(csq, "-2147483648"); // 11 char max.
} else if (i == 0) {
return csq.append('0');
}
i = -i;
csq.append('-');
}
int j = 1;
for (; (j < 10) && (i >= INT_POW_10[j]); j++) {
}
// POW_10[j] > i >= POW_10[j-1]
for (j--; j >= 0; j--) {
int pow10 = INT_POW_10[j];
int digit = i / pow10;
i -= digit * pow10;
csq.append(DIGIT_TO_CHAR[digit]);
}
return csq;
}
private static final int[] INT_POW_10 = new int[10];
static {
int pow = 1;
for (int i = 0; i < 10; i++) {
INT_POW_10[i] = pow;
pow *= 10;
}
}
/**
* Formats the specified int in the specified radix and appends
* the resulting text to the Appendable argument.
*
* @param i the int number.
* @param radix the radix.
* @param csq the Appendable to append.
* @return the specified Appendable object.
* @throws IllegalArgumentException if radix is not in [2 .. 36] range.
* @throws IOException if an I/O exception occurs.
* @see #parseInt(CharSequence, int)
*/
public static Appendable format(int i, int radix, Appendable csq)
throws IOException {
if (radix == 10) {
return format(i, csq); // Faster version.
} else if (radix < 2 || radix > 36) {
throw new IllegalArgumentException("radix: " + radix);
}
if (i < 0) {
csq.append('-');
} else {
i = -i;
}
format2(i, radix, csq);
return csq;
}
private static void format2(int i, int radix, Appendable csq)
throws IOException {
if (i <= -radix) {
format2(i / radix, radix, csq);
csq.append(DIGIT_TO_CHAR[-(i % radix)]);
} else {
csq.append(DIGIT_TO_CHAR[-i]);
}
}
/**
* Formats the specified long and appends the resulting
* text (decimal representation) to the Appendable argument.
*
* Note: This method is preferred to Appendable.append(long)
* as it does not create temporary String
* objects (several times faster for small numbers).
*
* @param l the long number.
* @param csq the Appendable to append.
* @return the specified Appendable object.
* @throws IOException if an I/O exception occurs.
* @see #parseLong
*/
public static Appendable format(long l, Appendable csq) throws IOException {
if (l <= 0) {
if (l == Long.MIN_VALUE) { // Negation would overflow.
return append(csq, "-9223372036854775808"); // 20 characters max.
} else if (l == 0) {
return csq.append('0');
}
l = -l;
csq.append('-');
}
int j = 1;
for (; (j < 19) && (l >= LONG_POW_10[j]); j++) {
}
// POW_10[j] > l >= POW_10[j-1]
for (j--; j >= 0; j--) {
long pow10 = LONG_POW_10[j];
int digit = (int) (l / pow10);
l -= digit * pow10;
csq.append(DIGIT_TO_CHAR[digit]);
}
return csq;
}
private static final long[] LONG_POW_10 = new long[19];
static {
long pow = 1;
for (int i = 0; i < 19; i++) {
LONG_POW_10[i] = pow;
pow *= 10;
}
}
/**
* Formats the specified long in the specified radix and
* appends the resulting text to the Appendable argument.
*
* @param l the long number.
* @param radix the radix.
* @param csq the Appendable to append.
* @return the specified Appendable object.
* @throws IllegalArgumentException if radix is not in [2 .. 36] range.
* @throws IOException if an I/O exception occurs.
* @see #parseLong(CharSequence, int)
*/
public static Appendable format(long l, int radix, Appendable csq)
throws IOException {
if (radix == 10) {
return format(l, csq); // Faster version.
} else if (radix < 2 || radix > 36) {
throw new IllegalArgumentException("radix: " + radix);
}
if (l < 0) {
csq.append('-');
} else {
l = -l;
}
format2(l, radix, csq);
return csq;
}
private static void format2(long l, int radix, Appendable csq)
throws IOException {
if (l <= -radix) {
format2(l / radix, radix, csq);
csq.append(DIGIT_TO_CHAR[(int) -(l % radix)]);
} else {
csq.append(DIGIT_TO_CHAR[(int) -l]);
}
}
/**
* Formats the specified float value.
*
* @param value the float value.
* @param csq the Appendable to append.
* @return format(value, 10, Math.abs(value) > 1E7, false, sb)
* @throws IOException if an I/O exception occurs.
* @see #format(double, int , boolean, boolean, Appendable)
/**/
public static Appendable format(float value, Appendable csq)
throws IOException {
return format(value, 10, MathLib.abs(value) > 1E7, false, csq);
}
/**/
/**
* Formats the specified double value.
*
* Note : This method is preferred to Double.toString(double)
* or even String.valueOf(double) as it
* does not create temporary String or
* FloatingDecimal objects (several times faster,
* e.g. 15x faster for Double.MAX_VALUE).
*
* @param value the double value.
* @param csq the Appendable to append.
* @return format(value, 17, Math.abs(value) > 1E7, false, sb)
* @throws IOException if an I/O exception occurs.
* @see #format(double, int , boolean, boolean, Appendable)
/**/
public static Appendable format(double value, Appendable csq)
throws IOException {
return format(value, 17, MathLib.abs(value) > 1E7, false, csq);
}
/**/
/**
* Formats the specified double value according to the
* specified formatting arguments.
*
* @param value the double value.
* @param digits the number of significative digits (excludes exponent).
* @param scientific true to forces the use of the scientific
* notation (e.g. 1.23E3); false
* otherwise.
* @param showZero true if trailing fractional zeros are
* represented; false otherwise.
* @param csq the Appendable to append.
* @return the specified Appendable object.
* @throws IllegalArgumentException if ((digits > 19) ||
* (digits <= 0)))
* @throws IOException if an I/O exception occurs.
/**/
public static Appendable format(double value, int digits,
boolean scientific, boolean showZero, Appendable csq) throws IOException {
if ((digits > 19) || (digits <= 0))
throw new IllegalArgumentException("digits: " + digits);
if (value != value) { // NaN
return append(csq, "NaN");
} else if (value == POSITIVE_INFINITY) {
return append(csq, "Infinity");
} else if (value == NEGATIVE_INFINITY) {
return append(csq, "-Infinity");
} else if (value == 0.0) {
if (digits == 1)
return append(csq, "0");
if (!showZero)
return append(csq, "0.0");
append(csq, "0.0");
for (int i = 2; i < digits; i++) {
csq.append('0');
}
return csq;
}
if (value < 0) {
value = -value;
csq.append('-');
}
// Find the exponent e such as: value == 0.xxx * 10^e
int e = (value >= 1.0) ? 1 + minPow10(value) : - minPow10(1.0 / value);
double digitValue = value * MathLib.toDouble(1, digits - e);
long mantissa = (long) (digitValue + 0.5);
if (scientific || (e <= 0) || (e > digits)) {
// Scientific notation has to be used ("x.xxxEyy").
format(mantissa / LONG_POW_10[digits - 1], csq);
formatFraction(mantissa % LONG_POW_10[digits - 1], digits - 1,
showZero, csq);
csq.append('E');
format(e - 1, csq);
} else if (e == digits) { // Dot at last position ("xxxxx").
format(mantissa, csq);
} else { // Dot within the string ("xxxx.xxxxx").
format(mantissa / LONG_POW_10[digits - e], csq);
formatFraction(mantissa % LONG_POW_10[digits - e], digits
- e, showZero, csq);
}
return csq;
}
private static final double POSITIVE_INFINITY = 1.0 / 0.0;
private static final double NEGATIVE_INFINITY = -1.0 / 0.0;
private static void formatFraction(long fraction, int digits,
boolean showZero, Appendable csq) throws IOException {
if (digits == 0)
return;
csq.append('.');
for (int i = digits; i > 0;) {
long pow10 = LONG_POW_10[--i];
int digit = (int) (fraction / pow10);
fraction -= digit * pow10;
csq.append(DIGIT_TO_CHAR[digit]);
if ((fraction == 0) && !showZero) {
return; // No more than one trailing zero.
}
}
}
// Returns e such as 10^e <= value < 10^(e+1), value >= 1.0
private static int minPow10(double value) {
int minE = 0;
int maxE = 308;
while (maxE - minE > 1) {
final int exp = (minE + maxE) >> 1;
if (value >= MathLib.toDouble(1, exp)) {
minE = exp;
} else {
maxE = exp;
}
}
return minE;
}
/**/
/**
* Appends the specified string argument to the specified appendable
* for backward compatibility when {@link String} was not a
* {@link CharSequence}.
*
* @param to the appendable.
* @param str the string to append.
* @return the specified appendable
* @throws IOException if an I/O exception occurs.
*/
private static Appendable append(Appendable to, String str)
throws IOException {
for (int i = 0; i < str.length(); i++) {
to.append(str.charAt(i));
}
return to;
}
}
Ads help maintain this website.