Mega Code Archive

 
Categories / Java / Data Type
 

Significant Figures

/*  * Copyright (C) 2002-2007 Stephen Ostermiller  * http://ostermiller.org/contact.pl?regarding=Java+Utilities  *  * This program is free software; you can redistribute it and/or modify  * it under the terms of the GNU General Public License as published by  * the Free Software Foundation; either version 2 of the License, or  * (at your option) any later version.  *  * This program 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.  *  * See COPYING.TXT for details.  */ /**  * A number with an associated number of significant figures.  * This class handles parsing numbers, determining the number  * of significant figures, adjusting the number of significant  * figures (including scientific rounding), and displaying the number.  * More information about this class is available from <a target="_top" href=  * "http://ostermiller.org/utils/SignificantFigures.html">ostermiller.org</a>.  * <p>  * When parsing a number to determine the number of significant figures,  * these rules are used:  * <ul>  * <li>Non-zero digits are always significant.</li>  * <li>All zeros between other significant digits are significant.</li>  * <li>All zeros left of the decimal point between a significant digit and the decimal point are significant.</li>  * <li>All trailing zeros to the right of the decimal point are significant.</li>  * <li>If the number is contains no digits other than zero, every zero is significant.</li>  * </ul>  * <p>  * When rounding a number the following rules are used:  * <ul>  * <li>If the greatest insignificant digit is less than five, round down.</li>  * <li>If the greatest insignificant digit is greater than five, round up.</li>  * <li>If the greatest insignificant digit is five and followed by some non-zero digit, round up.</li>  * <li>If the greatest insignificant digit is five and followed only by zeros, and the least significant  * digit is odd, round up.</li>  * <li>If the greatest insignificant digit is five and followed only by zeros, and the least significant  * digit is even, round down.</li>  * </ul>  *  * <p>  * Example of using this class to multiply numbers and display the result  * with the proper number of significant figures:<br>  * <pre> String[] arguments = {"1.0", "2.0", ...}  * SignificantFigures number;  * int sigFigs = Integer.MAX_VALUE;  * double result = 1D;  * for (int i=0; i&lt;arguments.length; i++){  * &nbsp;   number = new SignificantFigures(arguments[i]);  * &nbsp;   sigFigs = Math.min(sigFigs, number.getNumberSignificantFigures());  * &nbsp;   result *= number.doubleValue();  * }  * number = new SignificantFigures(result);  * number.setNumberSignificantFigures(sigFigs);  * System.out.println(number);</pre>  * <p>  * Example of using this class to add numbers and display the result  * with the proper number of significant figures:<br>  * <pre> String[] arguments = {"1.0", "2.0", ...}  * SignificantFigures number;  * int leastSD = Integer.MIN_VALUE;  * int mostSD = Integer.MIN_VALUE;  * double result = 0D;  * for (int i=0; i&lt;arguments.length; i++){  * &nbsp;   number = new SignificantFigures(arguments[i]);  * &nbsp;   leastSD = Math.max(leastSD, number.getLSD());  * &nbsp;   mostSD = Math.max(mostSD, number.getMSD());  * &nbsp;   result += number.doubleValue();  * }  * number = new SignificantFigures(result);  * number.setLMSD(leastSD, mostSD);  * System.out.println(number);</pre>  *  * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities  * @since ostermillerutils 1.00.00  */ public class SignificantFigures extends Number {   /**    *    */   private static final long serialVersionUID = -1130798283937219608L;   /**    * In the case the a number    * could not be parsed, the original is stored    * for toString purposes.    *    * @since ostermillerutils 1.00.00    */   private String original;   /**    * Buffer of the significant digits.    *    * @since ostermillerutils 1.00.00    */   private StringBuffer digits;   /**    * The exponent of the digits if a    * decimal place were inserted after    * the first digit.    *    * @since ostermillerutils 1.00.00    */   private int mantissa = -1;   /**    * positive if true, negative if false.    *    * @since ostermillerutils 1.00.00    */   private boolean sign = true;   /**    * True if this number has no non-zero digits.    *    * @since ostermillerutils 1.00.00    */   private boolean isZero = false;   /**    * Create a SignificantFigures object from a String representation of a number.    *    * @param number String representation of the number.    * @throws NumberFormatException if the String is not a valid number.    *    * @since ostermillerutils 1.00.00    */   public SignificantFigures(String number) throws NumberFormatException {     original = number;     parse(original);   }   /**    * Create a SignificantFigures object from a byte.    *    * @param number an 8 bit integer.    *    * @since ostermillerutils 1.00.00    */   public SignificantFigures(byte number){     original = Byte.toString(number);     try {       parse(original);     } catch (NumberFormatException nfe){       digits = null;     }   }   /**    * Create a SignificantFigures object from a short.    *    * @param number a 16 bit integer.    *    * @since ostermillerutils 1.00.00    */    public SignificantFigures(short number){     original = Short.toString(number);     try {       parse(original);     } catch (NumberFormatException nfe){       digits = null;     }   }   /**    * Create a SignificantFigures object from an integer.    *    * @param number a 32 bit integer.    *    * @since ostermillerutils 1.00.00    */   public SignificantFigures(int number){     original = String.valueOf(number);     try {       parse(original);     } catch (NumberFormatException nfe){       digits = null;     }   }   /**    * Create a SignificantFigures object from a long.    *    * @param number a 64 bit integer.    *    * @since ostermillerutils 1.00.00    */   public SignificantFigures(long number){     original = Long.toString(number);     try {       parse(original);     } catch (NumberFormatException nfe){       digits = null;     }   }   /**    * Create a SignificantFigures object from a float.    *    * @param number a 32 bit floating point.    *    * @since ostermillerutils 1.00.00    */   public SignificantFigures(float number){     original = Float.toString(number);     try {       parse(original);     } catch (NumberFormatException nfe){       digits = null;     }   }   /**    * Create a SignificantFigures object from a double.    *    * @param number a 64 bit floating point.    *    * @since ostermillerutils 1.00.00    */   public SignificantFigures(double number){     original = Double.toString(number);     try {       parse(original);     } catch (NumberFormatException nfe){       digits = null;     }   }   /**    * Create a SignificantFigures object from a java number such as    * a BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, or    * Short.    *    * @param number a number.    *    * @since ostermillerutils 1.00.00    */   public SignificantFigures(Number number){     original = number.toString();     try {       parse(original);     } catch (NumberFormatException nfe){       digits = null;     }   }   /**    * Get the number of significant digits.    * <p>    * If this number is not a number or infinity zero    * will be returned.    *    * @return the number of significant digits in this number.    *    * @since ostermillerutils 1.00.00    */   public int getNumberSignificantFigures() {     if (digits == null) return 0;     return digits.length();   }   /**    * Adjust the number of significant figures such that the least    * significant digit is at the given place.  This method may add    * significant zeros to the end of this number, or remove significant    * digits from this number.    * <p>    * It is possible to remove all significant digits from this number which    * will cause the string representation of this number to become "NaN".  This    * could become a problem if you are adding numbers and the result is close    * to zero.  All of the significant digits may get removed, even though the    * result could be zero with some number of significant digits.  Its is safes    * to use the setLMSD() method which will make a zero with the appropriate    * number of significant figures in such instances.    * <p>    * This method has no effect if this number is not a number or infinity.    *    * @param place the desired place of the least significant digit.    * @return this number.    *    * @since ostermillerutils 1.00.00    */   public SignificantFigures setLSD(int place){     setLMSD(place, Integer.MIN_VALUE);     return this;   }   /**    * Adjust the number of significant figures such that the least    * significant digit is at the given place.  This method may add    * significant zeros to the end of this number, or remove significant    * digits from this number.    * <p>    * If all significant digits are removed from this number by truncating to    * the least significant place, a zero will be created with significant figures    * from the least to most significant places.    * <p>    * This method has no effect if this number is not a number or infinity.    *    * @param leastPlace the desired place of the least significant digit or Integer.MIN_VALUE to ignore.    * @param mostPlace the desired place of the most significant digit or Integer.MIN_VALUE to ignore.    * @return this number    *    * @since ostermillerutils 1.00.00    */   public SignificantFigures setLMSD(int leastPlace, int mostPlace){     if (digits != null && leastPlace != Integer.MIN_VALUE){       int significantFigures = digits.length();       int current = mantissa - significantFigures + 1;       int newLength = significantFigures - leastPlace + current;       if (newLength <= 0){         if (mostPlace == Integer.MIN_VALUE){           original = "NaN";           digits = null;         } else {           newLength = mostPlace - leastPlace + 1;           digits.setLength(newLength);           mantissa = leastPlace;           for (int i=0; i<newLength; i++){             digits.setCharAt(i, '0');           }           isZero = true;           sign = true;         }       } else {         digits.setLength(newLength);         for (int i=significantFigures; i<newLength; i++){           digits.setCharAt(i, '0');         }       }     }     return this;   }   /**    * Get the decimal place of the least significant digit.    * <p>    * If this number is not a number or infinity Integer.MIN_VALUE    * will be returned.    *    * @return the decimal place of the least significant digit.    *    * @since ostermillerutils 1.00.00    */   public int getLSD(){     if (digits == null) return Integer.MIN_VALUE;     return mantissa - digits.length() + 1;   }   /**    * Get the decimal place of the most significant digit.    * <p>    * If this number is not a number or infinity Integer.MIN_VALUE    * will be returned.    *    * @return the decimal place of the least significant digit.    *    * @since ostermillerutils 1.00.00    */   public int getMSD(){     if (digits == null) return Integer.MIN_VALUE;     return mantissa + 1;   }   /**    * Formats this number.    * If the number is less than 10^-3 or greater than or equal to 10^7,    * or the number might have an ambiguous number of significant figures,    * scientific notation will be used.    * <p>    * A string such as "NaN" or "Infinity" may be returned by this method.    *    * @return representation of this number.    *    * @since ostermillerutils 1.00.00    */   @Override public String toString() {     if (digits == null) return original;     StringBuffer digits = new StringBuffer(this.digits.toString());     int length = digits.length();     if (mantissa <= -4 || mantissa >= 7 ||         (mantissa >= length &&         digits.charAt(digits.length()-1) == '0') ||         (isZero && mantissa != 0)) {       // use scientific notation.       if (length > 1){         digits.insert(1, '.');       }       if (mantissa != 0){         digits.append("E" + mantissa);       }     } else if (mantissa <= -1){       digits.insert(0, "0.");       for (int i=mantissa; i<-1; i++){         digits.insert(2, '0');       }     } else if (mantissa+1 == length){       if (length > 1 && digits.charAt(digits.length()-1) == '0'){         digits.append('.');       }     } else if (mantissa < length){       digits.insert(mantissa+1, '.');     } else {       for (int i=length; i<=mantissa; i++){         digits.append('0');       }     }     if (!sign) {       digits.insert(0, '-');     }     return digits.toString();   }   /**    * Formats this number in scientific notation.    * <p>    * A string such as "NaN" or "Infinity" may be returned by this method.    *    * @return representation of this number in scientific notation.    *    * @since ostermillerutils 1.00.00    */   public String toScientificNotation() {     if (digits == null) return original;     StringBuffer digits = new StringBuffer(this.digits.toString());     int length = digits.length();     if (length > 1){       digits.insert(1, '.');     }     if (mantissa != 0){       digits.append("E" + mantissa);     }     if (!sign) {       digits.insert(0, '-');     }     return digits.toString();   }   /**    * Parsing state:    * Initial state before anything read.    *    * @since ostermillerutils 1.00.00    */   private final static int INITIAL = 0;   /**    * Parsing state:    * State in which a possible sign and    * possible leading zeros have been read.    *    * @since ostermillerutils 1.00.00    */   private final static int LEADZEROS = 1;   /**    * Parsing state:    * State in which a possible sign and    * at least one non-zero digit    * has been read followed by some number of    * zeros.  The decimal place has no    * been encountered yet.    *    * @since ostermillerutils 1.00.00    */   private final static int MIDZEROS = 2;   /**    * Parsing state:    * State in which a possible sign and    * at least one non-zero digit    * has been read.  The decimal place has no    * been encountered yet.    *    * @since ostermillerutils 1.00.00    */   private final static int DIGITS = 3;   /**    * Parsing state:    * State in which only a possible sign,    * leading zeros, and a decimal point    * have been encountered.    *    * @since ostermillerutils 1.00.00    */   private final static int LEADZEROSDOT = 4;   /**    * Parsing state:    * State in which a possible sign,    * at least one nonzero digit and a    * decimal point have been encountered.    *    * @since ostermillerutils 1.00.00    */   private final static int DIGITSDOT = 5;   /**    * Parsing state:    * State in which the exponent symbol    * 'E' has been encountered.    *    * @since ostermillerutils 1.00.00    */   private final static int MANTISSA = 6;   /**    * Parsing state:    * State in which the exponent symbol    * 'E' has been encountered followed    * by a possible sign or some number    * of digits.    *    * @since ostermillerutils 1.00.00    */   private final static int MANTISSADIGIT = 7;   /**    * Parse a number from the given string.    * A valid number has an optional sign, some digits    * with an optional decimal point, and an optional    * scientific notation part consisting of an 'E' followed    * by an optional sign, followed by some digits.    *    * @param number String representation of a number.    * @throws NumberFormatException if the string is not a valid number.    *    * @since ostermillerutils 1.00.00    */   private void parse(String number) throws NumberFormatException {     int length = number.length();     digits = new StringBuffer(length);     int state = INITIAL;     int mantissaStart = -1;     boolean foundMantissaDigit = false;     // sometimes we don't know if a zero will be     // significant or not when it is encountered.     // keep track of the number of them so that     // the all can be made significant if we find     // out that they are.     int zeroCount = 0;     int leadZeroCount = 0;     for (int i=0; i<length; i++){       char c = number.charAt(i);       switch (c){         case '.': {           switch (state){             case INITIAL:             case LEADZEROS: {               state = LEADZEROSDOT;             } break;             case MIDZEROS: {               // we now know that these zeros               // are more than just trailing place holders.               for (int j=0; j<zeroCount; j++){                 digits.append('0');               }               zeroCount = 0;               state = DIGITSDOT;             } break;             case DIGITS: {               state = DIGITSDOT;             } break;             default: {               throw new NumberFormatException (                 "Unexpected character '" + c + "' at position " + i               );             }           }         } break;         case '+':{           switch (state){             case INITIAL: {               sign = true;               state = LEADZEROS;             } break;             case MANTISSA: {               state = MANTISSADIGIT;             } break;             default: {               throw new NumberFormatException (                 "Unexpected character '" + c + "' at position " + i               );             }           }         } break;         case '-': {           switch (state){             case INITIAL: {               sign = false;               state = LEADZEROS;             } break;             case MANTISSA: {               state = MANTISSADIGIT;             } break;             default: {               throw new NumberFormatException (                 "Unexpected character '" + c + "' at position " + i               );             }           }         } break;         case '0': {           switch (state){             case INITIAL:             case LEADZEROS: {               // only significant if number               // is all zeros.               zeroCount++;               leadZeroCount++;               state = LEADZEROS;             } break;             case MIDZEROS:             case DIGITS: {               // only significant if followed               // by a decimal point or nonzero digit.               mantissa++;               zeroCount++;               state = MIDZEROS;             } break;             case LEADZEROSDOT:{               // only significant if number               // is all zeros.               mantissa--;               zeroCount++;               state = LEADZEROSDOT;             } break;             case DIGITSDOT: {               // non-leading zeros after               // a decimal point are always               // significant.               digits.append(c);             } break;             case MANTISSA:             case MANTISSADIGIT: {               foundMantissaDigit = true;               state = MANTISSADIGIT;             } break;             default: {               throw new NumberFormatException (                 "Unexpected character '" + c + "' at position " + i               );             }           }         } break;         case '1': case '2': case '3':         case '4': case '5': case '6':         case '7': case '8': case '9': {           switch (state){             case INITIAL:             case LEADZEROS:             case DIGITS: {               zeroCount = 0;               digits.append(c);               mantissa++;               state = DIGITS;             } break;             case MIDZEROS: {               // we now know that these zeros               // are more than just trailing place holders.               for (int j=0; j<zeroCount; j++){                 digits.append('0');               }               zeroCount = 0;               digits.append(c);               mantissa++;               state = DIGITS;             } break;             case LEADZEROSDOT:             case DIGITSDOT: {               zeroCount = 0;               digits.append(c);               state = DIGITSDOT;             } break;             case MANTISSA:             case MANTISSADIGIT: {               state = MANTISSADIGIT;               foundMantissaDigit = true;             } break;             default: {               throw new NumberFormatException (                 "Unexpected character '" + c + "' at position " + i               );             }           }         } break;         case 'E': case 'e': {           switch (state){             case INITIAL:             case LEADZEROS:             case DIGITS:             case LEADZEROSDOT:             case DIGITSDOT: {               // record the starting point of the mantissa               // so we can do a substring to get it back later               mantissaStart = i+1;               state = MANTISSA;             } break;             default: {               throw new NumberFormatException (                 "Unexpected character '" + c + "' at position " + i               );             }           }         } break;         default: {           throw new NumberFormatException (             "Unexpected character '" + c + "' at position " + i           );         }       }     }     if (mantissaStart != -1){       // if we had found an 'E'       if (!foundMantissaDigit){         // we didn't actually find a mantissa to go with.         throw new NumberFormatException (           "No digits in mantissa."         );       }       // parse the mantissa.       mantissa += Integer.parseInt(number.substring(mantissaStart));     }     if (digits.length() == 0){       if (zeroCount > 0){         // if nothing but zeros all zeros are significant.         for (int j=0; j<zeroCount; j++){           digits.append('0');         }         mantissa += leadZeroCount;         isZero = true;         sign = true;       } else {         // a hack to catch some cases that we could catch         // by adding a ton of extra states.  Things like:         // "e2" "+e2" "+." "." "+" etc.         throw new NumberFormatException (           "No digits in number."         );       }     }   }   /**    * Adjust the number of digits in the number.    * Pad the tail with zeros if too short, round the    * number according to scientific rounding if too long, leave alone    * if just right.    * <p>    * This method has no effect if this number is not a number or infinity.    *    * @param significantFigures desired number of significant figures.    * @return This number.    *    * @since ostermillerutils 1.00.00    */   public SignificantFigures setNumberSignificantFigures(int significantFigures){     if (significantFigures <= 0) throw new IllegalArgumentException("Desired number of significant figures must be positive.");     if (digits != null) {       int length =  digits.length();       if (length < significantFigures){         // number is not long enough, pad it with zeros.         for (int i=length; i<significantFigures; i++){           digits.append('0');         }       } else if (length > significantFigures){         // number is too long chop some of it off with rounding.         boolean addOne; // we need to round up if true.         char firstInSig = digits.charAt(significantFigures);         if (firstInSig < '5'){           // first non-significant digit less than five, round down.           addOne = false;         } else if (firstInSig == '5'){           // first non-significant digit equal to five           addOne = false;           for (int i=significantFigures+1; !addOne && i<length; i++){             // if its followed by any non-zero digits, round up.             if (digits.charAt(i) != '0'){               addOne = true;             }           }           if (!addOne){             // if it was not followed by non-zero digits             // if the last significant digit is odd round up             // if the last significant digit is even round down             addOne = (digits.charAt(significantFigures-1) & 1) == 1;           }         } else {           // first non-significant digit greater than five, round up.           addOne = true;         }         // loop to add one (and carry a one if added to a nine)         // to the last significant digit         for (int i=significantFigures-1; addOne && i>=0; i--){           char digit = digits.charAt(i);           if (digit < '9'){             digits.setCharAt(i, (char)(digit+1));             addOne = false;           } else {             digits.setCharAt(i, '0');           }         }         if (addOne){           // if the number was all nines           digits.insert(0, '1');           mantissa++;         }         // chop it to the correct number of figures.         digits.setLength(significantFigures);       }     }     return this;   }   /**    * Returns the value of this number as a byte.    *    * @return the numeric value represented by this object after conversion to type byte.    * @throws NumberFormatException if this number cannot be converted to a byte.    *    * @since ostermillerutils 1.00.00    */   @Override public byte byteValue() throws NumberFormatException {     return Byte.parseByte(original);   }   /**    * Returns the value of this number as a double.    *    * @return the numeric value represented by this object after conversion to type double.    * @throws NumberFormatException if this number cannot be converted to a double.    *    * @since ostermillerutils 1.00.00    */   @Override public double doubleValue() throws NumberFormatException {     return Double.parseDouble(original);   }   /**    * Returns the value of this number as a float.    *    * @return the numeric value represented by this object after conversion to type float.    * @throws NumberFormatException if this number cannot be converted to a float.    *    * @since ostermillerutils 1.00.00    */   @Override public float floatValue() throws NumberFormatException {     return Float.parseFloat(original);   }   /**    * Returns the value of this number as a int.    *    * @return the numeric value represented by this object after conversion to type int.    * @throws NumberFormatException if this number cannot be converted to a int.    *    * @since ostermillerutils 1.00.00    */   @Override public int intValue() throws NumberFormatException {     return Integer.parseInt(original);   }   /**    * Returns the value of this number as a long.    *    * @return the numeric value represented by this object after conversion to type long.    * @throws NumberFormatException if this number cannot be converted to a long.    *    * @since ostermillerutils 1.00.00    */   @Override public long longValue() throws NumberFormatException {     return Long.parseLong(original);   }   /**    * Returns the value of this number as a short.    *    * @return the numeric value represented by this object after conversion to type short.    * @throws NumberFormatException if this number cannot be converted to a short.    *    * @since ostermillerutils 1.00.00    */   @Override public short shortValue() throws NumberFormatException {     return Short.parseShort(original);   }   /**    * Convenience method to display a number with the correct    * significant digits.    *    * @param number the number to display    * @param significantFigures the number of significant figures to display.    * @return the number formatted with the correct significant figures    *    * @since ostermillerutils 1.02.07    */   public static String format(byte number, int significantFigures){     SignificantFigures sf = new SignificantFigures(number);     sf.setNumberSignificantFigures(significantFigures);     return sf.toString();   }   /**    * Convenience method to display a number with the correct    * significant digits.    *    * @param number the number to display    * @param significantFigures the number of significant figures to display.    * @return the number formatted with the correct significant figures    *    * @since ostermillerutils 1.02.07    */   public static String format(double number, int significantFigures){     SignificantFigures sf = new SignificantFigures(number);     sf.setNumberSignificantFigures(significantFigures);     return sf.toString();   }   /**    * Convenience method to display a number with the correct    * significant digits.    *    * @param number the number to display    * @param significantFigures the number of significant figures to display.    * @return the number formatted with the correct significant figures    *    * @since ostermillerutils 1.02.07    */   public static String format(float number, int significantFigures){     SignificantFigures sf = new SignificantFigures(number);     sf.setNumberSignificantFigures(significantFigures);     return sf.toString();   }   /**    * Convenience method to display a number with the correct    * significant digits.    *    * @param number the number to display    * @param significantFigures the number of significant figures to display.    * @return the number formatted with the correct significant figures    *    * @since ostermillerutils 1.02.07    */   public static String format(int number, int significantFigures){     SignificantFigures sf = new SignificantFigures(number);     sf.setNumberSignificantFigures(significantFigures);     return sf.toString();   }   /**    * Convenience method to display a number with the correct    * significant digits.    *    * @param number the number to display    * @param significantFigures the number of significant figures to display.    * @return the number formatted with the correct significant figures    *    * @since ostermillerutils 1.02.07    */   public static String format(long number, int significantFigures){     SignificantFigures sf = new SignificantFigures(number);     sf.setNumberSignificantFigures(significantFigures);     return sf.toString();   }   /**    * Convenience method to display a number with the correct    * significant digits.    *    * @param number the number to display    * @param significantFigures the number of significant figures to display.    * @return the number formatted with the correct significant figures    *    * @since ostermillerutils 1.02.07    */   public static String format(Number number, int significantFigures){     SignificantFigures sf = new SignificantFigures(number);     sf.setNumberSignificantFigures(significantFigures);     return sf.toString();   }   /**    * Convenience method to display a number with the correct    * significant digits.    *    * @param number the number to display    * @param significantFigures the number of significant figures to display.    * @return the number formatted with the correct significant figures    *    * @since ostermillerutils 1.02.07    */   public static String format(short number, int significantFigures){     SignificantFigures sf = new SignificantFigures(number);     sf.setNumberSignificantFigures(significantFigures);     return sf.toString();   }   /**    * Convenience method to display a number with the correct    * significant digits.    *    * @param number the number to display    * @param significantFigures the number of significant figures to display.    * @return the number formatted with the correct significant figures    * @throws NumberFormatException if the String is not a valid number.    *    * @since ostermillerutils 1.02.07    */   public static String format(String number, int significantFigures) throws NumberFormatException {     SignificantFigures sf = new SignificantFigures(number);     sf.setNumberSignificantFigures(significantFigures);     return sf.toString();   } }