umontreal.iro.lecuyer.util.PrintfFormat Maven / Gradle / Ivy
Show all versions of ssj Show documentation
/*
* Class: PrintfFormat
* Description:
* Environment: Java
* Software: SSJ
* Copyright (C) 2001 Pierre L'Ecuyer and Université de Montréal
* Organization: DIRO, Université de Montréal
* @author
* @since
* SSJ is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License (GPL) as published by the
* Free Software Foundation, either version 3 of the License, or
* any later version.
* SSJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* A copy of the GNU General Public License is available at
GPL licence site.
*/
package umontreal.iro.lecuyer.util;
import java.text.NumberFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.Formatter;
/**
* This class acts like a {@link StringBuffer} which defines new types
* of append methods.
* It defines certain functionalities of the ANSI C printf
* function that also can be accessed through static methods.
* The information given here is strongly inspired
* from the man page of the C printf function.
*
*
* Most methods of this class format numbers for the English US
* locale only. One can use the Java class {@link java.util.Formatter Formatter}
* for performing locale-independent formatting.
*
*/
public class PrintfFormat implements CharSequence, Appendable {
private static NumberFormat nf = NumberFormat.getInstance (Locale.US);
private static DecimalFormatSymbols dfs =
new DecimalFormatSymbols (Locale.US);
private static final int NDEC = 50;
private static DecimalFormat[] dfe = new DecimalFormat[NDEC+1];
private static DecimalFormat[] dfg = new DecimalFormat[NDEC+1];
private StringBuffer sb;
/*
private static void round (int b, StringBuffer num) {
// round a real number up if it is >= 0.5
// base is b
// round up the fractional part
int j = num.length() - 1;
String bm1 = String.valueOf (b - 1);
while (j >= 0 && num.charAt(j) == bm1.charAt(0)) {
num.deleteCharAt(j);
j--;
}
char c;
if (j < 0)
return;
if (num.charAt(j) != '.') {
c = num.charAt(j);
++c;
num.replace(j, j + 1, Character.toString(c));
return;
}
// round up the integral part
j--;
while (j >= 0 && num.charAt(j) == bm1.charAt(0)) {
num.replace(j, j + 1, "0");
j--;
}
if (j < 0 || num.charAt(j) == '-') {
num.insert( j + 1, '1');
} else if (num.charAt(j) == ' ') {
num.replace(j, j + 1, "1");
} else {
c = num.charAt(j);
++c;
num.replace(j, j + 1, Character.toString(c));
}
}*/
/**
* End-of-line symbol or line separator. It is ``
* \n''
* for Unix/Linux, ``
* \r
* \n" for MS-DOS/MS-Windows, and
* ``
* \r'' for Apple OSX.
*
*/
public static final String NEWLINE =
System.getProperty("line.separator");
/**
* Same as NEWLINE.
*
*/
@Deprecated
public static final String LINE_SEPARATOR =
System.getProperty("line.separator");
/**
* Constructs a new buffer object containing an empty string.
*
*/
public PrintfFormat() {
sb = new StringBuffer();
}
/**
* Constructs a new buffer object with an initial capacity of length.
*
* @param length initial length of the buffer
*
*
*/
public PrintfFormat (int length) {
sb = new StringBuffer (length);
}
/**
* Constructs a new buffer object containing the initial string str.
*
* @param str initial contents of the buffer
*
*/
public PrintfFormat (String str) {
sb = new StringBuffer (str);
}
/**
* Appends str to the buffer.
*
* @param str string to append to the buffer
*
* @return this object
*
*/
public PrintfFormat append (String str) {
sb.append (str);
return this;
}
/**
* Uses the {@link #s(int,String) s} static method to append str to the buffer.
* A minimum of fieldwidth characters will be used.
*
* @param fieldwidth minimum number of characters that will be added to the buffer
*
* @param str string to append to the buffer
*
* @return this object
*
*/
public PrintfFormat append (int fieldwidth, String str) {
sb.append (s (fieldwidth, str));
return this;
}
/**
* Appends x to the buffer.
*
* @param x value being added to the buffer
*
* @return this object
*
*/
public PrintfFormat append (double x) {
sb.append (x);
return this;
}
/**
* Uses the {@link #f(int,double) f} static method to append x to the buffer.
* A minimum of fieldwidth characters will be used.
*
* @param fieldwidth minimum length of the converted string to be appended
*
* @param x value to be appended to the buffer
*
* @return this object
*
*/
public PrintfFormat append (int fieldwidth, double x) {
sb.append (f (fieldwidth, x));
return this;
}
/**
* Uses the {@link #f(int,int,double) f} static method to append x to the buffer.
* A minimum of fieldwidth characters will be used with the given precision.
*
* @param fieldwidth minimum length of the converted string to be appended
*
* @param precision number of digits after the decimal point of the converted value
*
* @param x value to be appended to the buffer
*
* @return this object
*
*/
public PrintfFormat append (int fieldwidth, int precision, double x) {
sb.append (f (fieldwidth, precision, x));
return this;
}
/**
* Appends x to the buffer.
*
* @param x value to be appended to the buffer
*
* @return this object
*
*/
public PrintfFormat append (int x) {
sb.append (x);
return this;
}
/**
* Uses the {@link #d(int,long) d} static method to append x to the buffer.
* A minimum of fieldwidth characters will be used.
*
* @param fieldwidth minimum length of the converted string to be appended
*
* @param x value to be appended to the buffer
*
* @return this object
*
*/
public PrintfFormat append (int fieldwidth, int x) {
sb.append (d (fieldwidth, x));
return this;
}
/**
* Appends x to the buffer.
*
* @param x value to be appended to the buffer
*
* @return this object
*
*/
public PrintfFormat append (long x) {
sb.append (x);
return this;
}
/**
* Uses the {@link #d(int,long) d} static method to append x to the buffer.
* A minimum of fieldwidth characters will be used.
*
* @param fieldwidth minimum length of the converted string to be appended
*
* @param x value to be appended to the buffer
*
* @return this object
*
*/
public PrintfFormat append (int fieldwidth, long x) {
sb.append (d (fieldwidth, x));
return this;
}
/**
* Uses the {@link #format(int,int,int,double) format}
* static method with the same four arguments to append x to the buffer.
*
* @param fieldwidth minimum length of the converted string to be appended
*
* @param accuracy number of digits after the decimal point
*
* @param precision number of significant digits
*
* @param x value to be appended to the buffer
*
* @return this object
*
*/
public PrintfFormat append (int fieldwidth, int accuracy, int precision,
double x) {
sb.append (format (fieldwidth, accuracy, precision, x));
return this;
}
/**
* Appends a single character to the buffer.
*
* @param c character to be appended to the buffer
*
* @return this object
*
*/
public PrintfFormat append (char c) {
sb.append (c);
return this;
}
/**
* Clears the contents of the buffer.
*
*/
public void clear() {
sb.setLength (0);
}
/**
* Returns the {@link StringBuffer} associated with that object.
*
* @return the internal {@link StringBuffer} object
*
*/
public StringBuffer getBuffer() {
return sb;
}
/**
* Converts the buffer into a {@link String}.
*
* @return the {@link String} conversion of the internal buffer
*
*/
public String toString() {
return sb.toString();
}
/**
* Same as {@link #s(int,String) s} (0, str). If the string str is null,
* it returns the string ``null''.
*
* @param str the string to process
*
* @return the same string
*
*/
public static String s (String str) {
if (str == null)
return "null";
else
return str;
}
/**
* Formats the string str like the %s in the C printf
* function. The fieldwidth argument gives the minimum length of the
* resulting string. If str is shorter than fieldwidth, it is
* left-padded with spaces. If fieldwidth is negative, the string
* is right-padded with spaces if necessary. The {@link String} will never
* be truncated. If str is null, it calls
* {@link #s(int, String) s} (fieldwidth, ``null'').
* The fieldwidth argument
* has the same effect for the other methods in this class.
*
* @param fieldwidth minimum length of the returned string
*
* @param str the string to process
*
* @return the same string padded with spaces if necessary
*
*/
public static String s (int fieldwidth, String str) {
if (str == null)
return s (fieldwidth, "null");
int fw = Math.abs (fieldwidth);
if (str.length() < fw) {
// We have to pad with spaces
StringBuffer buf = new StringBuffer();
int sl = str.length();
for (int i = 0; i < fw-sl; i++)
buf.append (' ');
// Add the spaces before or after
return fieldwidth >= 0 ? buf.toString() + str
: str + buf.toString();
}
else
return str;
}
/**
* Same as {@link #d(int,int,long) d} (0, 1, x).
*
* @param x the string to process
*
* @return the same string, padded with spaces or zeros if appropriate
*
*/
public static String d (long x) {
return d (0, 1, x);
}
/**
* Same as {@link #d(int,int,long) d} (fieldwidth, 1, x).
*
* @param fieldwidth minimum length of the returned string
*
* @param x the string to process
*
* @return the same string, padded with spaces or zeros if appropriate
*
*/
public static String d (int fieldwidth, long x) {
return d (fieldwidth, 1, x);
}
/**
* Formats the long integer x into a string like %d
* in the C printf function. It converts its argument to decimal
* notation, precision gives the minimum
* number of digits that must appear; if the converted
* value requires fewer digits, it is padded on the
* left with zeros. When
* zero is printed with an explicit precision 0, the
* output is empty.
*
* @param fieldwidth minimum length of the returned string
*
* @param precision number of digits in the returned string
*
* @param x the string to process
*
* @return the same string, padded with spaces or zeros if appropriate
*
*/
public static String d (int fieldwidth, int precision, long x) {
if (precision < 0)
throw new IllegalArgumentException ("precision must " +
"not be negative.");
if (precision == 0 && x == 0)
return s (fieldwidth, "");
nf.setGroupingUsed (false);
nf.setMinimumIntegerDigits (precision);
nf.setMaximumFractionDigits (0); // will also set the min to 0
return s (fieldwidth, nf.format (x));
}
/**
* Same as {@link #d(int,int,long) d} (0, 1, x).
*
* @param x the value to be processed
*
* @return the string resulting from the conversion
*
*/
public static String format (long x) {
return d (0, 1, x);
}
/**
* Converts a long integer to a {@link String} with a minimum length
* of fieldwidth, the result is right-padded with spaces if
* necessary but it is not truncated. If only one argument is specified,
* a fieldwidth of 0 is assumed.
*
* @param fieldwidth minimum length of the returned string
*
* @param x the value to be processed
*
* @return the string resulting from the conversion
*
*/
public static String format (int fieldwidth, long x) {
return d (fieldwidth, 1, x);
}
/**
* Same as {@link #formatBase(int,int,long) formatBase} (0, b, x).
*
* @param b the base used for conversion
*
* @param x the value to be processed
*
* @return a string representing x in base b
*
*/
public static String formatBase (int b, long x) {
return formatBase (0, b, x);
}
/**
* Converts the integer x to a {@link String} representation in base
* b. Restrictions: 2 <= b <= 10.
*
* @param fieldwidth minimum length of the returned string
*
* @param b the base used for conversion
*
* @param x the value to be processed
*
* @return a string representing x in base b
*
*/
public static String formatBase (int fieldwidth, int b, long x) {
boolean neg = false; // insert a '-' if true
if (b < 2 || b > 10)
throw new IllegalArgumentException ("base must be between 2 and 10.");
if (x < 0) {
neg = true;
x = -x;
} else {
if (x == 0)
return "0";
neg = false;
}
StringBuffer sb = new StringBuffer();
while (x > 0) {
sb.insert(0, x % b);
x = x/b;
}
if (neg)
sb.insert(0, '-');
return s (fieldwidth, sb.toString());
}
/**
* Same as {@link #E(int,int,double) E} (0, 6, x).
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String E (double x) {
return E (0, 6, x);
}
/**
* Same as {@link #E(int,int,double) E} (fieldwidth, 6, x).
*
* @param fieldwidth minimum length of the returned string
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String E (int fieldwidth, double x) {
return E (fieldwidth, 6, x);
}
/**
* Formats a double-precision number x like %E in C printf. The double argument is rounded and converted in the
* style [-]d.dddE+-dd where there is one digit before
* the decimal-point character and the number of digits
* after it is equal to the precision;
* if the precision is 0, no decimal-point character appears.
* The exponent always
* contains at least two digits; if the value is zero,
* the exponent is 00.
*
* @param fieldwidth minimum length of the returned string
*
* @param precision number of digits after the decimal point
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String E (int fieldwidth, int precision, double x) {
if (precision < 0)
throw new IllegalArgumentException ("precision must " +
"not be negative.");
if (Double.isNaN (x))
return s (fieldwidth, "NaN");
if (Double.isInfinite (x))
return s (fieldwidth, (x < 0 ? "-" : "") + "Infinite");
DecimalFormat df;
if (precision >= dfe.length || dfe[precision] == null) {
// We need to create one pattern per precision value
StringBuffer pattern = new StringBuffer ("0.");
for (int i = 0; i < precision; i++)
pattern.append ("0");
pattern.append ("E00");
df = new DecimalFormat (pattern.toString(), dfs);
df.setGroupingUsed (false);
if (precision < dfe.length)
dfe[precision] = df;
}
else
df = dfe[precision];
String res = df.format (x);
// DecimalFormat doesn't add the + sign before the value of
// the exponent.
int exppos = res.indexOf ('E');
if (exppos != -1 && res.charAt (exppos+1) != '-')
res = res.substring (0, exppos+1) + "+" + res.substring (exppos+1);
return s (fieldwidth, res);
}
/**
* Same as {@link #e(int,int,double) e} (0, 6, x).
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String e (double x) {
return e (0, 6, x);
}
/**
* Same as {@link #e(int,int,double) e} (fieldwidth, 6, x).
*
* @param fieldwidth minimum length of the returned string
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String e (int fieldwidth, double x) {
return e (fieldwidth, 6, x);
}
/**
* The same as E, except that `e' is used as the exponent
* character instead of `E'.
*
* @param fieldwidth minimum length of the returned string
*
* @param precision number of digits after the decimal point
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String e (int fieldwidth, int precision, double x) {
String res = E (fieldwidth, precision, x);
int exppos = res.indexOf ('E');
return exppos == -1 ? res : res.substring (0,
exppos) + 'e' + res.substring (exppos+1);
}
/**
* Same as {@link #f(int,int,double) f} (0, 6, x).
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String f (double x) {
return f (0, 6, x);
}
/**
* Same as {@link #f(int,int,double) f} (fieldwidth, 6, x).
*
* @param fieldwidth minimum length of the returned string
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String f (int fieldwidth, double x) {
return f (fieldwidth, 6, x);
}
/**
* Formats the double-precision x into a string like
* %f in C printf.
* The argument is rounded and converted to
* decimal notation in the style [-]ddd.ddd, where the
* number of digits after the decimal-point character
* is equal to the precision specification.
* If the precision is explicitly 0, no decimal-point
* character appears. If a decimal point appears, at least
* one digit appears before it.
*
* @param fieldwidth minimum length of the returned string
*
* @param precision number of digits after the decimal point
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String f (int fieldwidth, int precision, double x) {
if (precision < 0)
throw new IllegalArgumentException ("precision must " +
"not be negative.");
if (Double.isNaN (x))
return s (fieldwidth, "NaN");
if (Double.isInfinite (x))
return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
nf.setGroupingUsed (false);
nf.setMinimumIntegerDigits (1);
nf.setMinimumFractionDigits (precision);
nf.setMaximumFractionDigits (precision);
return s (fieldwidth, nf.format (x));
}
/**
* Same as {@link #G(int,int,double) G} (0, 6, x).
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String G (double x) {
return G (0, 6, x);
}
/**
* Same as {@link #G(int,int,double) G} (fieldwidth, 6, x).
*
* @param fieldwidth minimum length of the returned string
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String G (int fieldwidth, double x) {
return G (fieldwidth, 6, x);
}
/**
* Formats the double-precision x into a string like
* %G in C printf.
* The argument is converted in style %f or %E.
* precision specifies
* the number of significant digits. If it is
* 0, it is treated as 1. Style %E is used if the
* exponent from its conversion is less than -4 or
* greater than or equal to precision. Trailing
* zeros are removed from the fractional part of the
* result; a decimal point appears only if it is followed
* by at least one digit.
*
* @param fieldwidth minimum length of the returned string
*
* @param precision number of significant digits
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String G (int fieldwidth, int precision, double x) {
if (precision < 0)
throw new IllegalArgumentException ("precision must " +
"not be negative.");
if (precision == 0)
precision = 1;
if (Double.isNaN (x))
return s (fieldwidth, "NaN");
if (Double.isInfinite (x))
return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
// Calculate the scientific notation format.
// We cannot use E because it can make trailing zeros
// that must be removed afterward.
DecimalFormat df;
if (precision >= dfg.length || dfg[precision] == null) {
StringBuffer pattern = new StringBuffer ("0.");
for (int i = 0; i < (precision-1); i++)
pattern.append ("#"); // Do not show trailing zeros
pattern.append ("E00");
df = new DecimalFormat (pattern.toString(), dfs);
df.setGroupingUsed (false);
if (precision < dfg.length)
dfg[precision] = df;
}
else
df = dfg[precision];
String res = df.format (x);
int exppos = res.indexOf ('E');
if (exppos == -1)
return res;
int expval = Integer.parseInt (res.substring (exppos+1));
if (expval < -4 || expval >= precision) {
// add the plus sign for the exponent if necessary.
if (res.charAt (exppos+1) != '-')
return s (fieldwidth, res.substring (0, exppos+1) + "+" +
res.substring (exppos+1));
else
return s (fieldwidth, res);
}
// Calculate the decimal notation format
nf.setGroupingUsed (false);
nf.setMinimumIntegerDigits (1);
nf.setMinimumFractionDigits (0);
// We need the number of digits after the decimal point to
// to get precisions significant digits.
// The integer part of x contains at most precision digits.
// If that was not true, expval would be greater than precision.
// If expval=0, we have a number of the form 1.234...
// To have precisions significant digits, we need precision-1 digits.
// If expval<0, x<1 and we need -expval additionnal
// decimal digits.
// If expval>0, we need less decimal digits.
nf.setMaximumFractionDigits (precision-expval-1);
res = nf.format (x);
return s (fieldwidth, res);
}
/**
* Same as {@link #g(int,int,double) g} (0, 6, x).
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String g (double x) {
return g (0, 6, x);
}
/**
* Same as {@link #g(int,int,double) g} (fieldwidth, 6, x).
*
* @param fieldwidth minimum length of the returned string
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String g (int fieldwidth, double x) {
return g (fieldwidth, 6, x);
}
/**
* The same as G, except that `e' is used in the scientific
* notation.
*
* @param fieldwidth minimum length of the returned string
*
* @param precision number of significant digits
*
* @param x the value to be converted to string
*
* @return the converted value as a string
*
*/
public static String g (int fieldwidth, int precision, double x) {
String res = G (fieldwidth, precision, x);
int exppos = res.indexOf ('E');
return exppos == -1 ? res :
res.substring (0, exppos) + 'e' + res.substring (exppos+1);
}
/**
* Returns a {@link String} containing x. Uses a total of at least
* fieldwidth positions (including the sign and point when they appear),
* accuracy digits after the decimal point and at least precision
* significant digits. accuracy and precision must be strictly
* smaller than fieldwidth. The number is rounded if necessary.
* If there is not enough space to format the number in decimal notation
* with at least precision significant digits (accuracy or
* fieldwidth is too small), it will be converted to scientific
* notation with at least precision significant digits.
* In that case, fieldwidth is increased if necessary.
*
* @param fieldwidth minimum length of the returned string
*
* @param accuracy number of digits after the decimal point
*
* @param precision number of significant digits
*
* @param x the value to be processed
*
* @return the converted value as a string
*
*/
public static String format (int fieldwidth, int accuracy, int precision,
double x) {
if (Double.isNaN (x))
return s (fieldwidth, "NaN");
if (Double.isInfinite (x))
return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
if (canUseDecimalNotation (fieldwidth, accuracy, precision, x))
return f (fieldwidth, accuracy, x);
// Use scientific notation
else {
String S = E (fieldwidth, precision - 1 , x);
return processExp (S);
}
}
private static boolean canUseDecimalNotation (int fieldwidth, int accuracy,
int precision, double x) {
// Le nombre de positions occupees par la partie entiere de x
int PosEntier = 0;
// Le nombre de chiffres significatifs avant le point
int EntierSign;
// La position de l'exposant dans le string S et la longueur de S
int Neg = 0;
if (x == 0.0)
EntierSign = 1;
else {
EntierSign = PosEntier = (int)Math.floor (
Math.log10 (Math.abs (x)) + 1);
if (x < 0.0)
Neg = 1;
}
if (EntierSign <= 0)
PosEntier = 1;
return x == 0.0 || (((EntierSign + accuracy) >= precision) &&
(fieldwidth >= (PosEntier + accuracy + Neg + 1)));
}
private static int getMinAccuracy (double x) {
if (Math.abs (x) >= 1 || x == 0)
return 0;
else
return -(int)Math.floor (Math.log10 (Math.abs (x)));
}
private static String processExp (String s) {
int p = s.indexOf ("E+0");
if (p == -1)
p = s.indexOf ("E-0");
// remove the 0 in E-0 and in E+0
if (p != -1)
s = " " + s.substring (0, p + 2) + s.substring (p + 3);
p = s.indexOf (".E");
if (p != -1)
s = " " + s.substring (0, p) + s.substring (p + 1);
return s;
}
/**
* This method is equivalent to
* {@link #format(int,int,int,double) format}, except it formats the given
* value for the locale locale.
*
* @param locale the locale being used for formatting
*
* @param fieldwidth minimum length of the returned string
*
* @param accuracy number of digits after the decimal point
*
* @param precision number of significant digits
*
* @param x the value to be processed
*
* @return the converted value as a string
*
*/
public static String format (Locale locale, int fieldwidth, int accuracy,
int precision, double x) {
Formatter fmt = new Formatter (locale);
if (Double.isNaN (x))
return fmt.format ("%" + fieldwidth + "s", "NaN").toString();
if (Double.isInfinite (x))
return fmt.format ("%" + fieldwidth + "s", (x < 0 ? "-" : "" ) + "Infinite").toString();
if (canUseDecimalNotation (fieldwidth, accuracy, precision, x))
return fmt.format ("%" + fieldwidth + "." + accuracy + "f", x).toString();
// Use scientific notation
else {
String S = fmt.format ("%" + fieldwidth + "." + (precision - 1) + "E", x).toString();
return processExp (S);
}
}
/**
* Converts x to a String representation in base b using formatting similar
* to the f methods. Uses a total of at least fieldwidth positions
* (including the sign and point when they appear) and accuracy digits
* after the decimal point. If fieldwidth is negative, the number is
* printed left-justified, otherwise right-justified.
* Restrictions:
* 2 <= b <= 10 and
* | x| < 263.
*
* @param fieldwidth minimum length of the returned string
*
* @param accuracy number of digits after the decimal point
*
* @param b base
*
* @param x the value to be processed
*
* @return the converted value as a string
*
*
*/
public static String formatBase (int fieldwidth, int accuracy, int b,
double x) {
if (Double.isNaN (x))
return s (fieldwidth, "NaN");
if (Double.isInfinite (x))
return s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
if (0. == x || -0. == x)
return s (fieldwidth, "0");
if (Math.abs(x) >= Num.TWOEXP[63])
throw new UnsupportedOperationException (" |x| >= 2^63");
long n = (long) x;
String mant = formatBase(-1, b, n);
if (n == x)
return s (fieldwidth, mant);
if (n == 0) {
if (x < 0) {
mant = "-0";
} else
mant = "0";
}
// round before printing
if (x > 0)
x += 0.5*Math.pow(b, -accuracy - 1);
else if (x < 0)
x -= 0.5*Math.pow(b, -accuracy - 1);
x -= n;
if (x < 0)
x = -x;
StringBuffer frac = new StringBuffer(".");
long y;
int j;
for (j = 0; j < accuracy; ++j) {
x *= b;
y = (long) x;
frac.append(y);
x -= y;
if (x == 0.)
break;
}
StringBuffer number = new StringBuffer(mant);
number.append(frac);
// remove trailing spaces and 0
j = number.length() - 1;
while (j > 0 && (number.charAt(j) == '0' || number.charAt(j) == ' ' )) {
number.deleteCharAt(j);
j--;
}
return s (fieldwidth, number.toString());
}
// Interface CharSequence
public char charAt (int index) {
return sb.charAt (index);
}
public int length() {
return sb.length();
}
public CharSequence subSequence (int start, int end) {
return sb.subSequence (start, end);
}
// Interface Appendable
public Appendable append (CharSequence csq) {
return sb.append (csq);
}
public Appendable append (CharSequence csq, int start, int end) {
return sb.append (csq, start, end);
}
/**
* Stores a string containing x into res[0], and
* a string containing error into res[1], both strings being
* formatted with the same notation.
* Uses a total of at least
* fieldwidth positions (including the sign and point when they appear)
* for x, fieldwidtherr positions for error,
* accuracy digits after the decimal point and at least precision
* significant digits. accuracy and precision must be strictly
* smaller than fieldwidth. The numbers are rounded if necessary.
* If there is not enough space to format x in decimal notation
* with at least precision significant digits (accuracy or
* fieldwidth are too small), it will be converted to scientific
* notation with at least precision significant digits.
* In that case, fieldwidth is increased if necessary, and
* the error is also formatted in scientific notation.
*
* @param fieldwidth minimum length of the value string
*
* @param fieldwidtherr minimum length of the error string
*
* @param accuracy number of digits after the decimal point for the value and error
*
* @param precision number of significant digits for the value
*
* @param x the value to be processed
*
* @param error the error on the value to be processed
*
* @param res an array that will be filled with the formatted value and formatted error
*
*
*/
public static void formatWithError (int fieldwidth, int fieldwidtherr,
int accuracy, int precision, double x, double error, String[] res) {
if (res.length != 2)
throw new IllegalArgumentException ("The given res array must contain two elements");
if (Double.isNaN (x)) {
res[0] = s (fieldwidth, "NaN");
res[1] = s (fieldwidtherr, "");
return;
}
if (Double.isInfinite (x)) {
res[0] = s (fieldwidth, (x < 0 ? "-" : "" ) + "Infinite");
res[1] = s (fieldwidtherr, "");
return;
}
if (accuracy < 0)
accuracy = 0;
if (canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
res[0] = f (fieldwidth, accuracy, x);
res[1] = f (fieldwidtherr, accuracy, error);
}
// Use scientific notation
else {
res[0] = processExp (E (fieldwidth, precision - 1, x));
int xExp = x == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (x)));
int errorExp = error == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (error)));
int errorPrecision = precision - 1 - (xExp - errorExp);
if (errorPrecision < 0)
errorPrecision = 0;
res[1] = processExp (E (fieldwidtherr, errorPrecision, error));
}
}
/**
* Stores a string containing x into res[0], and
* a string containing error into res[1], both strings being
* formatted with the same notation.
* This calls {@link #formatWithError(int,int,int,int,double,double,String[]) formatWithError} with
* the minimal accuracy for which the formatted string for error is non-zero.
* If error is 0, the accuracy is 0.
* If this minimal accuracy causes the strings to be formatted using scientific
* notation, this method increases the accuracy until the decimal notation can be used.
*
* @param fieldwidth minimum length of the value string
*
* @param fieldwidtherr minimum length of the error string
*
* @param precision number of significant digits for the value
*
* @param x the value to be processed
*
* @param error the error on the value to be processed
*
* @param res an array that will be filled with the formatted value and formatted error
*
*
*/
public static void formatWithError (int fieldwidth, int fieldwidtherr,
int precision, double x, double error, String[] res) {
int accuracy = getMinAccuracy (error);
if (!canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
int posEntier = (int)Math.floor (Math.log (Math.abs (x)) / Math.log (10) + 1);
if (posEntier < 0)
posEntier = 1;
int newAccuracy = precision - posEntier;
if (canUseDecimalNotation (fieldwidth, newAccuracy, precision, x))
accuracy = newAccuracy;
}
formatWithError (fieldwidth, fieldwidtherr, accuracy, precision, x, error, res);
}
/**
* This method is equivalent to
* {@link #formatWithError(int,int,int,double,double,String[]) formatWithError},
* except that it formats the given value and error for the
* locale locale.
*
* @param locale the locale being used
*
* @param fieldwidth minimum length of the value string
*
* @param fieldwidtherr minimum length of the error string
*
* @param accuracy number of digits after the decimal point for the value and error
*
* @param precision number of significant digits for the value
*
* @param x the value to be processed
*
* @param error the error on the value to be processed
*
* @param res an array that will be filled with the formatted value and formatted error
*
*
*/
public static void formatWithError (Locale locale, int fieldwidth,
int fieldwidtherr, int accuracy, int precision, double x,
double error, String[] res) {
if (res.length != 2)
throw new IllegalArgumentException ("The given res array must contain two elements");
Formatter fmt = new Formatter (locale);
Formatter fmtErr = new Formatter (locale);
if (Double.isNaN (x)) {
res[0] = fmt.format ("%" + fieldwidth + "s", "NaN").toString();
res[1] = fmtErr.format ("%" + fieldwidtherr + "s", "").toString();
return;
}
if (Double.isInfinite (x)) {
res[0] = fmt.format ("%" + fieldwidth + "s", (x < 0 ? "-" : "" ) + "Infinite").toString();
res[1] = fmtErr.format ("%" + fieldwidtherr + "s", "").toString();
return;
}
if (accuracy < 0)
accuracy = 0;
if (canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
res[0] = fmt.format ("%" + fieldwidth + "." + accuracy + "f", x).toString();
res[1] = fmtErr.format ("%" + fieldwidtherr + "." + accuracy + "f", error).toString();
}
// Use scientific notation
else {
res[0] = processExp (fmt.format ("%" + fieldwidth + "." + (precision - 1) + "E", x).toString());
int xExp = x == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (x)));
int errorExp = error == 0 ? 0 : (int)Math.floor (Math.log10 (Math.abs (error)));
int errorPrecision = precision - 1 - (xExp - errorExp);
if (errorPrecision < 0)
errorPrecision = 0;
res[1] = processExp (fmtErr.format
("%" + fieldwidtherr + "." + errorPrecision + "E", error).toString());
}
}
/**
* This method is equivalent to
* {@link #formatWithError(int,int,int,double,double,String[]) formatWithError},
* except that it formats the given value and error for the
* locale locale.
*
* @param locale the locale being used
*
* @param fieldwidth minimum length of the value string
*
* @param fieldwidtherr minimum length of the error string
*
* @param precision number of significant digits for the value
*
* @param x the value to be processed
*
* @param error the error on the value to be processed
*
* @param res an array that will be filled with the formatted value and formatted error
*
*/
public static void formatWithError (Locale locale, int fieldwidth,
int fieldwidtherr, int precision, double x, double error,
String[] res) {
int accuracy = getMinAccuracy (error);
if (!canUseDecimalNotation (fieldwidth, accuracy, precision, x)) {
int posEntier = (int)Math.floor (Math.log (Math.abs (x)) / Math.log (10) + 1);
if (posEntier < 0)
posEntier = 1;
int newAccuracy = precision - posEntier;
if (canUseDecimalNotation (fieldwidth, newAccuracy, precision, x))
accuracy = newAccuracy;
}
formatWithError (locale, fieldwidth, fieldwidtherr, accuracy, precision, x, error, res);
}
}