package org.xmind.org.freehep.util;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.Locale;
/**
* This code formats numbers in Scientific Notation. The input Number object is
* returned as a ScientificFormated string. There are two output styles: Pure
* and Standard scientific notation. Pure formatted numbers have precisely the
* number of digits specified by the significant digits (sigDig) parameter and
* always specify a Base 10 Exponential(E). Standard formated numbers have the
* number of digits specified by the significant digits (sigDig) parameter but
* will not have a Base 10 Exponential(E) if the number of digits in the
* mantissa <= maxWidth.
*
* @author Paul Spence
* @author Mark Donszelmann
* @author Jason Wong
*/
public class ScientificFormat extends Format {
/**
* The number of significant digits the number is formatted to is recorded
* by sigDigit. The maximum width allowed for the returned String is
* recorded by MaxWidth
*/
private int sigDigit = 5;
private int maxWidth = 8;
private boolean sciNote = false; // set to true for pure Scientific Notation
private DecimalFormat decimalFormat;
private static final long serialVersionUID = -1182686857248711235L;
public ScientificFormat() {
}
/**
* Sets the significant digits, maximum allowable width and number
* formatting style (SciNote == true for Pure formatting).
*/
public ScientificFormat(int sigDigit, int maxWidth, boolean SciNote) {
setSigDigits(sigDigit);
setMaxWidth(maxWidth);
setScientificNotationStyle(SciNote);
}
/**
* Implementation of inherited abstract method. Checks to see if object to
* be formatted is of type Number. If so casts the Number object to double
* and calls the format method. Returns the result.
*/
public StringBuffer format(Object obj, StringBuffer toAppendTo,
FieldPosition pos) {
if (obj instanceof Number) {
String result = format(((Number) obj).doubleValue());
return toAppendTo.append(result);
} else if (obj instanceof DoubleWithError) {
DoubleWithError dwe = (DoubleWithError) obj;
toAppendTo.append(format(dwe.getValue()));
if (dwe.hasAsymmetricError()) {
toAppendTo.append(DoubleWithError.plus);
int errorSigDigit = resolveErrorSigDigit(dwe.getValue(),
dwe.getPlusError());
toAppendTo.append(format(dwe.getPlusError(), errorSigDigit));
toAppendTo.append(DoubleWithError.minus);
errorSigDigit = resolveErrorSigDigit(dwe.getValue(),
dwe.getMinError());
toAppendTo.append(format(dwe.getMinError(), errorSigDigit));
} else {
toAppendTo.append(DoubleWithError.plusorminus);
int errorSigDigit = resolveErrorSigDigit(dwe.getValue(),
dwe.getError());
toAppendTo.append(format(dwe.getError(), errorSigDigit));
}
return toAppendTo;
} else
throw new IllegalArgumentException(
"Cannot format given Object as a Number"); //$NON-NLS-1$
}
/**
* Dummy implementation of inherited abstract method.
*/
public Object parseObject(String source, ParsePosition pos) {
return null;
}
/**
* Returns the number of significant digits
*/
public int getSigDigits() {
return sigDigit;
}
/**
* Returns the maximum allowable width of formatted number excluding any
* exponentials
*/
public int getMaxWidth() {
return maxWidth;
}
/**
* Returns the formatting style: True means Pure scientific formatting,
* False means standard.
*/
public boolean getScientificNotationStyle() {
return sciNote;
}
/**
* Sets the number of significant digits for the formatted number
*/
public void setSigDigits(int SigDigit) {
if (SigDigit < 1)
throw new IllegalArgumentException("sigDigit"); //$NON-NLS-1$
sigDigit = SigDigit;
decimalFormat = null;
}
/**
* Sets the maximum allowable length of the formattted number mantissa
* before exponential notation is used.
*/
public void setMaxWidth(int mWidth) {
if (mWidth < 3)
throw new IllegalArgumentException("maxWidth"); //$NON-NLS-1$
maxWidth = mWidth;
}
/**
* Sets the format style used. There are two output styles: Pure and
* Standard scientific notation. Pure formatted numbers have precisely the
* number of digits specified by the significant digits (sigDig) parameter
* and always specify a Base 10 Exponential(E). Standard formated numbers
* have the number of digits specified by the significant digits (sigDig)
* parameter but will not have a Base 10 Exponential(E) if the number of
* digits in the mantissa <= maxWidth.
*/
public void setScientificNotationStyle(boolean sciNote) {
this.sciNote = sciNote;
}
// simplify method for taking log base 10 of x
private final static double k = 1 / Math.log(10);
private double Log10(double x) {
if (x == 0)
return 0;
else
return Math.log(x) * k;
}
private int resolveErrorSigDigit(double x, double dx) {
// dx should never be negative
dx = Math.abs(dx);
// make x +ve cause negative doesn't effect sigdigits
x = Math.abs(x);
// these circumstances errorsigdit does equal sigdigit, excluding
// infinity and Nan which are handled by format
if (dx == 0 || Double.isInfinite(dx) || Double.isNaN(dx) || dx >= x)
return sigDigit;
// fail cases for log, method fails to handle
if (x == 0 || Double.isInfinite(x) || Double.isNaN(x))
return sigDigit;
// otherwise solve for cases when dx<x
int log = (int) Math.round(Log10(dx / x));// always will return negative
// number
int errorsigdigit = sigDigit + log;
if (errorsigdigit < 1)
return 1;
return errorsigdigit;
}
private DecimalFormat getDecimalFormat(int sigDig) {
StringBuffer buffer = new StringBuffer("0."); //$NON-NLS-1$
for (int i = 1; i < sigDig; i++)
buffer.append('0');
buffer.append("E0"); //$NON-NLS-1$
return new DecimalFormat(buffer.toString(), new DecimalFormatSymbols(
Locale.US));
}
/**
* Format the number using scientific notation
*/
public String format(double d) {
return format(d, sigDigit);
}
private String format(double d, int sigDig) {
// Deal with a few special values first
if (Double.isInfinite(d))
return maxWidth < 8 ? "INF" : "Infinite"; //$NON-NLS-1$ //$NON-NLS-2$
if (Double.isNaN(d))
return "NaN"; //$NON-NLS-1$
// Delegate the hard part to decimalFormat
if (decimalFormat == null)
decimalFormat = getDecimalFormat(sigDigit);
DecimalFormat format = (sigDig == sigDigit) ? decimalFormat
: getDecimalFormat(sigDig);
String preliminaryResult = format.format(d);
if (sciNote)
return preliminaryResult;
int ePos = preliminaryResult.indexOf('E');
int exponent = Integer.parseInt(preliminaryResult.substring(ePos + 1)) + 1;
if (exponent > maxWidth)
return preliminaryResult;
if (exponent < -maxWidth + sigDig + 1)
return preliminaryResult;
// We need to fix up the result
int sign = preliminaryResult.charAt(0) == '-' ? 1 : 0;
StringBuffer result = new StringBuffer(preliminaryResult.substring(
sign, sign + 1) + preliminaryResult.substring(sign + 2, ePos));
if (exponent >= sigDig) {
for (int i = sigDig; i < exponent; i++)
result.append('0');
} else if (exponent < 0) {
result.insert(0, "."); //$NON-NLS-1$
for (int i = exponent; i < 0; i++)
result.insert(1, '0');
} else {
result.insert(exponent, '.');
}
if (sign > 0)
result.insert(0, '-');
return result.toString();
}
// /**
// * Format a number plus error using scientific notation
// */
// public String formatError(double d,double dx)
// {
// return format(dx, resolveErrorSigDigit(d, dx));
// }
}