package jas.util;
import java.math.BigDecimal;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
/**
* 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
* @version 03/20/2000
*/
public class ScientificFormat extends Format
{
/**
* The number of significant digits the number is formatted to is recorded by sigDigit.
* The maximum width allowed fro 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
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()));
toAppendTo.append(dwe.plusorminus);
int errorSigDigit = resolveErrorSigDigit(dwe.getValue(),dwe.getError());
toAppendTo.append(formatError(errorSigDigit,dwe.getError()));
return toAppendTo;
}
else throw new IllegalArgumentException("Cannot format given Object as a Number");
}
/**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");
sigDigit = SigDigit;
}
/**
* 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");
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)
{
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;
//other wise 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;
}
/**
* Format the number using scientific notation
*/
public String format(double d)
{
// Deal with a few special values first
if (Double.isInfinite(d)) return maxWidth < 8 ? "INF" : "Infinite";
if (Double.isNaN(d)) return "NaN";
String result="";
double c=0;
int ShiftNumber = 0;
int IntOfNumberinput = 0;
//preserve sign
if (d==0) return "0";
else {
c=d;
d=Math.abs(c);
}
// (i.e. 10 -> 1, 100 -> 2,9 -> 0 , .9 -> -1, .0009 -> -4)
//error here 10 -> 1.0000 floors to 0
IntOfNumberinput = (int) Math.floor(Log10(d));//returns largest int value that is smaller than log10(d)
//deal with above error
if((IntOfNumberinput>-1)&&(d%(Math.pow(10,IntOfNumberinput+1)) == 0)){
IntOfNumberinput++;
}
//if 0<d<1 then log10(d)is neg, IntofNumberinput is negative
if (Log10(d)<0){
ShiftNumber = sigDigit - IntOfNumberinput - 1;
}else {
ShiftNumber = sigDigit - IntOfNumberinput;
}
//outputs num with all sigdigs to right of decimal place or rounded up one extra
long temp = Math.round(Math.pow(10,ShiftNumber)*d);
String Formatted = String.valueOf(temp);
//check rounding method, if neccessary add 1 to IntOfNumberinput
BigDecimal tempbunk =new BigDecimal(Math.pow(10,ShiftNumber)*d);
long bunk =tempbunk.longValue();
String Formattedbunk = String.valueOf(bunk);
if(Formatted.length() > Formattedbunk.length()){
IntOfNumberinput++;
}
//Do not display in pure sci notattion - limit use of E
if (SciNote == false)
{
if (IntOfNumberinput < 0 ) {
String LoopZero1="";
for (int a=0; a<(Math.abs(IntOfNumberinput)-1);a++){
LoopZero1=LoopZero1+"0";
}
result="0"+"."+LoopZero1+Formatted;
}
else{
String[] FillDigits = new String[IntOfNumberinput+1];
for(int a=0;a<=IntOfNumberinput;a++){
FillDigits[a]="0";
}
int a = 0;
while((a < Formatted.length()) && a<=IntOfNumberinput){
FillDigits[a] = Formatted.substring(a,a+1);
a++;
}
for(int i=0; i <= FillDigits.length-1 ; i++){
result = result + FillDigits[i];
}
int length = result.length();
if(length <sigDigit){
String resultaddon ="";
int i=-1;
if(IntOfNumberinput==0){
while(length < sigDigit){
resultaddon = resultaddon + Formatted.substring(result.length()+i+1,result.length()+i+2);
length++;
i++;
}
}else{
i = 0;
while(length < sigDigit){
resultaddon = resultaddon + Formatted.substring(result.length()+i,result.length()+i+1);
length++;
i++;
}
}
result = result+"."+resultaddon;
}
}
if(result.length()>maxWidth){
result=Formatted.substring(0,1)+"."+Formatted.substring(1,sigDigit)+"E"+IntOfNumberinput;
}
}
//output in pure Scientific Notation
if(SciNote == true){
result=Formatted.substring(0,1)+"."+Formatted.substring(1,sigDigit)+"E"+IntOfNumberinput;
}
//regain negative and return
if(c>0) return result;
else return "-"+result;
}
/**
* Format the number using scientific notation
*/
public String formatError(int eSD,double d)
{
// Deal with a few special values first
if (Double.isInfinite(d)) return maxWidth < 8 ? "INF" : "Infinite";
if (Double.isNaN(d)) return "NaN";
int errorSigDigit = eSD;
String result="";
double c=0;
int ShiftNumber = 0;
int IntOfNumberinput = 0;
//preserve sign
if (d==0) return "0";
else {
c=d;
d=Math.abs(c);
}
// (i.e. 10 -> 1, 100 -> 2,9 -> 0 , .9 -> -1, .0009 -> -4)
//error here 10 -> 1.0000 floors to 0
IntOfNumberinput = (int) Math.floor(Log10(d));//returns largest int value that is smaller than log10(d)
//deal with above error
if((IntOfNumberinput>-1)&&(d%(Math.pow(10,IntOfNumberinput+1)) == 0)){
IntOfNumberinput++;
}
//if 0<d<1 then log10(d)is neg, IntofNumberinput is negative
if (Log10(d)<0){
ShiftNumber = errorSigDigit - IntOfNumberinput - 1;
}else {
ShiftNumber = errorSigDigit - IntOfNumberinput;
}
//outputs num with all sigdigs to right of decimal place or rounded up one extra
long temp = Math.round(Math.pow(10,ShiftNumber)*d);
String Formatted = String.valueOf(temp);
//check rounding method, if neccessary add 1 to IntOfNumberinput
BigDecimal tempbunk =new BigDecimal(Math.pow(10,ShiftNumber)*d);
long bunk =tempbunk.longValue();
String Formattedbunk = String.valueOf(bunk);
if(Formatted.length() > Formattedbunk.length()){
IntOfNumberinput++;
}
//Do not display in pure sci notattion - limit use of E
if (SciNote == false)
{
if (IntOfNumberinput < 0 ) {
String LoopZero1="";
for (int a=0; a<(Math.abs(IntOfNumberinput)-1);a++){
LoopZero1=LoopZero1+"0";
}
result="0"+"."+LoopZero1+Formatted;
}
else{
String[] FillDigits = new String[IntOfNumberinput+1];
for(int a=0;a<=IntOfNumberinput;a++){
FillDigits[a]="0";
}
int a = 0;
while((a < Formatted.length()) && a<=IntOfNumberinput){
FillDigits[a] = Formatted.substring(a,a+1);
a++;
}
for(int i=0; i <= FillDigits.length-1 ; i++){
result = result + FillDigits[i];
}
int length = result.length();
if(length <errorSigDigit){
String resultaddon ="";
int i=-1;
if(IntOfNumberinput==0){
while(length < errorSigDigit){
resultaddon = resultaddon + Formatted.substring(result.length()+i+1,result.length()+i+2);
length++;
i++;
}
}else{
i = 0;
while(length < errorSigDigit){
resultaddon = resultaddon + Formatted.substring(result.length()+i,result.length()+i+1);
length++;
i++;
}
}
result = result+"."+resultaddon;
}
}
if(result.length()>maxWidth){
result=Formatted.substring(0,1)+"."+Formatted.substring(1,errorSigDigit)+"E"+IntOfNumberinput;
}
}
//output in pure Scientific Notation
if(SciNote == true){
result=Formatted.substring(0,1)+"."+Formatted.substring(1,errorSigDigit)+"E"+IntOfNumberinput;
}
//regain negative and return
if(c>0) return result;
else return "-"+result;
}
}