0001: /*
0002: * Copyright (C) 2002-2007 Stephen Ostermiller
0003: * http://ostermiller.org/contact.pl?regarding=Java+Utilities
0004: *
0005: * This program is free software; you can redistribute it and/or modify
0006: * it under the terms of the GNU General Public License as published by
0007: * the Free Software Foundation; either version 2 of the License, or
0008: * (at your option) any later version.
0009: *
0010: * This program is distributed in the hope that it will be useful,
0011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0013: * GNU General Public License for more details.
0014: *
0015: * See COPYING.TXT for details.
0016: */
0017:
0018: package com.Ostermiller.util;
0019:
0020: /**
0021: * A number with an associated number of significant figures.
0022: * This class handles parsing numbers, determining the number
0023: * of significant figures, adjusting the number of significant
0024: * figures (including scientific rounding), and displaying the number.
0025: * More information about this class is available from <a target="_top" href=
0026: * "http://ostermiller.org/utils/SignificantFigures.html">ostermiller.org</a>.
0027: * <p>
0028: * When parsing a number to determine the number of significant figures,
0029: * these rules are used:
0030: * <ul>
0031: * <li>Non-zero digits are always significant.</li>
0032: * <li>All zeros between other significant digits are significant.</li>
0033: * <li>All zeros left of the decimal point between a significant digit and the decimal point are significant.</li>
0034: * <li>All trailing zeros to the right of the decimal point are significant.</li>
0035: * <li>If the number is contains no digits other than zero, every zero is significant.</li>
0036: * </ul>
0037: * <p>
0038: * When rounding a number the following rules are used:
0039: * <ul>
0040: * <li>If the greatest insignificant digit is less than five, round down.</li>
0041: * <li>If the greatest insignificant digit is greater than five, round up.</li>
0042: * <li>If the greatest insignificant digit is five and followed by some non-zero digit, round up.</li>
0043: * <li>If the greatest insignificant digit is five and followed only by zeros, and the least significant
0044: * digit is odd, round up.</li>
0045: * <li>If the greatest insignificant digit is five and followed only by zeros, and the least significant
0046: * digit is even, round down.</li>
0047: * </ul>
0048: *
0049: * <p>
0050: * Example of using this class to multiply numbers and display the result
0051: * with the proper number of significant figures:<br>
0052: * <pre> String[] arguments = {"1.0", "2.0", ...}
0053: * SignificantFigures number;
0054: * int sigFigs = Integer.MAX_VALUE;
0055: * double result = 1D;
0056: * for (int i=0; i<arguments.length; i++){
0057: * number = new SignificantFigures(arguments[i]);
0058: * sigFigs = Math.min(sigFigs, number.getNumberSignificantFigures());
0059: * result *= number.doubleValue();
0060: * }
0061: * number = new SignificantFigures(result);
0062: * number.setNumberSignificantFigures(sigFigs);
0063: * System.out.println(number);</pre>
0064: * <p>
0065: * Example of using this class to add numbers and display the result
0066: * with the proper number of significant figures:<br>
0067: * <pre> String[] arguments = {"1.0", "2.0", ...}
0068: * SignificantFigures number;
0069: * int leastSD = Integer.MIN_VALUE;
0070: * int mostSD = Integer.MIN_VALUE;
0071: * double result = 0D;
0072: * for (int i=0; i<arguments.length; i++){
0073: * number = new SignificantFigures(arguments[i]);
0074: * leastSD = Math.max(leastSD, number.getLSD());
0075: * mostSD = Math.max(mostSD, number.getMSD());
0076: * result += number.doubleValue();
0077: * }
0078: * number = new SignificantFigures(result);
0079: * number.setLMSD(leastSD, mostSD);
0080: * System.out.println(number);</pre>
0081: *
0082: * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
0083: * @since ostermillerutils 1.00.00
0084: */
0085: public class SignificantFigures extends Number {
0086:
0087: /**
0088: *
0089: */
0090: private static final long serialVersionUID = -1130798283937219608L;
0091: /**
0092: * In the case the a number
0093: * could not be parsed, the original is stored
0094: * for toString purposes.
0095: *
0096: * @since ostermillerutils 1.00.00
0097: */
0098: private String original;
0099: /**
0100: * Buffer of the significant digits.
0101: *
0102: * @since ostermillerutils 1.00.00
0103: */
0104: private StringBuffer digits;
0105: /**
0106: * The exponent of the digits if a
0107: * decimal place were inserted after
0108: * the first digit.
0109: *
0110: * @since ostermillerutils 1.00.00
0111: */
0112: private int mantissa = -1;
0113: /**
0114: * positive if true, negative if false.
0115: *
0116: * @since ostermillerutils 1.00.00
0117: */
0118: private boolean sign = true;
0119: /**
0120: * True if this number has no non-zero digits.
0121: *
0122: * @since ostermillerutils 1.00.00
0123: */
0124: private boolean isZero = false;
0125:
0126: /**
0127: * Create a SignificantFigures object from a String representation of a number.
0128: *
0129: * @param number String representation of the number.
0130: * @throws NumberFormatException if the String is not a valid number.
0131: *
0132: * @since ostermillerutils 1.00.00
0133: */
0134: public SignificantFigures(String number)
0135: throws NumberFormatException {
0136: original = number;
0137: parse(original);
0138: }
0139:
0140: /**
0141: * Create a SignificantFigures object from a byte.
0142: *
0143: * @param number an 8 bit integer.
0144: *
0145: * @since ostermillerutils 1.00.00
0146: */
0147: public SignificantFigures(byte number) {
0148: original = Byte.toString(number);
0149: try {
0150: parse(original);
0151: } catch (NumberFormatException nfe) {
0152: digits = null;
0153: }
0154: }
0155:
0156: /**
0157: * Create a SignificantFigures object from a short.
0158: *
0159: * @param number a 16 bit integer.
0160: *
0161: * @since ostermillerutils 1.00.00
0162: */
0163: public SignificantFigures(short number) {
0164: original = Short.toString(number);
0165: try {
0166: parse(original);
0167: } catch (NumberFormatException nfe) {
0168: digits = null;
0169: }
0170: }
0171:
0172: /**
0173: * Create a SignificantFigures object from an integer.
0174: *
0175: * @param number a 32 bit integer.
0176: *
0177: * @since ostermillerutils 1.00.00
0178: */
0179: public SignificantFigures(int number) {
0180: original = String.valueOf(number);
0181: try {
0182: parse(original);
0183: } catch (NumberFormatException nfe) {
0184: digits = null;
0185: }
0186: }
0187:
0188: /**
0189: * Create a SignificantFigures object from a long.
0190: *
0191: * @param number a 64 bit integer.
0192: *
0193: * @since ostermillerutils 1.00.00
0194: */
0195: public SignificantFigures(long number) {
0196: original = Long.toString(number);
0197: try {
0198: parse(original);
0199: } catch (NumberFormatException nfe) {
0200: digits = null;
0201: }
0202: }
0203:
0204: /**
0205: * Create a SignificantFigures object from a float.
0206: *
0207: * @param number a 32 bit floating point.
0208: *
0209: * @since ostermillerutils 1.00.00
0210: */
0211: public SignificantFigures(float number) {
0212: original = Float.toString(number);
0213: try {
0214: parse(original);
0215: } catch (NumberFormatException nfe) {
0216: digits = null;
0217: }
0218: }
0219:
0220: /**
0221: * Create a SignificantFigures object from a double.
0222: *
0223: * @param number a 64 bit floating point.
0224: *
0225: * @since ostermillerutils 1.00.00
0226: */
0227: public SignificantFigures(double number) {
0228: original = Double.toString(number);
0229: try {
0230: parse(original);
0231: } catch (NumberFormatException nfe) {
0232: digits = null;
0233: }
0234: }
0235:
0236: /**
0237: * Create a SignificantFigures object from a java number such as
0238: * a BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, or
0239: * Short.
0240: *
0241: * @param number a number.
0242: *
0243: * @since ostermillerutils 1.00.00
0244: */
0245: public SignificantFigures(Number number) {
0246: original = number.toString();
0247: try {
0248: parse(original);
0249: } catch (NumberFormatException nfe) {
0250: digits = null;
0251: }
0252: }
0253:
0254: /**
0255: * Get the number of significant digits.
0256: * <p>
0257: * If this number is not a number or infinity zero
0258: * will be returned.
0259: *
0260: * @return the number of significant digits in this number.
0261: *
0262: * @since ostermillerutils 1.00.00
0263: */
0264: public int getNumberSignificantFigures() {
0265: if (digits == null)
0266: return 0;
0267: return digits.length();
0268: }
0269:
0270: /**
0271: * Adjust the number of significant figures such that the least
0272: * significant digit is at the given place. This method may add
0273: * significant zeros to the end of this number, or remove significant
0274: * digits from this number.
0275: * <p>
0276: * It is possible to remove all significant digits from this number which
0277: * will cause the string representation of this number to become "NaN". This
0278: * could become a problem if you are adding numbers and the result is close
0279: * to zero. All of the significant digits may get removed, even though the
0280: * result could be zero with some number of significant digits. Its is safes
0281: * to use the setLMSD() method which will make a zero with the appropriate
0282: * number of significant figures in such instances.
0283: * <p>
0284: * This method has no effect if this number is not a number or infinity.
0285: *
0286: * @param place the desired place of the least significant digit.
0287: * @return this number.
0288: *
0289: * @since ostermillerutils 1.00.00
0290: */
0291: public SignificantFigures setLSD(int place) {
0292: setLMSD(place, Integer.MIN_VALUE);
0293: return this ;
0294: }
0295:
0296: /**
0297: * Adjust the number of significant figures such that the least
0298: * significant digit is at the given place. This method may add
0299: * significant zeros to the end of this number, or remove significant
0300: * digits from this number.
0301: * <p>
0302: * If all significant digits are removed from this number by truncating to
0303: * the least significant place, a zero will be created with significant figures
0304: * from the least to most significant places.
0305: * <p>
0306: * This method has no effect if this number is not a number or infinity.
0307: *
0308: * @param leastPlace the desired place of the least significant digit or Integer.MIN_VALUE to ignore.
0309: * @param mostPlace the desired place of the most significant digit or Integer.MIN_VALUE to ignore.
0310: * @return this number
0311: *
0312: * @since ostermillerutils 1.00.00
0313: */
0314: public SignificantFigures setLMSD(int leastPlace, int mostPlace) {
0315: if (digits != null && leastPlace != Integer.MIN_VALUE) {
0316: int significantFigures = digits.length();
0317: int current = mantissa - significantFigures + 1;
0318: int newLength = significantFigures - leastPlace + current;
0319: if (newLength <= 0) {
0320: if (mostPlace == Integer.MIN_VALUE) {
0321: original = "NaN";
0322: digits = null;
0323: } else {
0324: newLength = mostPlace - leastPlace + 1;
0325: digits.setLength(newLength);
0326: mantissa = leastPlace;
0327: for (int i = 0; i < newLength; i++) {
0328: digits.setCharAt(i, '0');
0329: }
0330: isZero = true;
0331: sign = true;
0332: }
0333: } else {
0334: digits.setLength(newLength);
0335: for (int i = significantFigures; i < newLength; i++) {
0336: digits.setCharAt(i, '0');
0337: }
0338: }
0339: }
0340: return this ;
0341: }
0342:
0343: /**
0344: * Get the decimal place of the least significant digit.
0345: * <p>
0346: * If this number is not a number or infinity Integer.MIN_VALUE
0347: * will be returned.
0348: *
0349: * @return the decimal place of the least significant digit.
0350: *
0351: * @since ostermillerutils 1.00.00
0352: */
0353: public int getLSD() {
0354: if (digits == null)
0355: return Integer.MIN_VALUE;
0356: return mantissa - digits.length() + 1;
0357: }
0358:
0359: /**
0360: * Get the decimal place of the most significant digit.
0361: * <p>
0362: * If this number is not a number or infinity Integer.MIN_VALUE
0363: * will be returned.
0364: *
0365: * @return the decimal place of the least significant digit.
0366: *
0367: * @since ostermillerutils 1.00.00
0368: */
0369: public int getMSD() {
0370: if (digits == null)
0371: return Integer.MIN_VALUE;
0372: return mantissa + 1;
0373: }
0374:
0375: /**
0376: * Formats this number.
0377: * If the number is less than 10^-3 or greater than or equal to 10^7,
0378: * or the number might have an ambiguous number of significant figures,
0379: * scientific notation will be used.
0380: * <p>
0381: * A string such as "NaN" or "Infinity" may be returned by this method.
0382: *
0383: * @return representation of this number.
0384: *
0385: * @since ostermillerutils 1.00.00
0386: */
0387: @Override
0388: public String toString() {
0389: if (digits == null)
0390: return original;
0391: StringBuffer digits = new StringBuffer(this .digits.toString());
0392: int length = digits.length();
0393: if (mantissa <= -4
0394: || mantissa >= 7
0395: || (mantissa >= length && digits
0396: .charAt(digits.length() - 1) == '0')
0397: || (isZero && mantissa != 0)) {
0398: // use scientific notation.
0399: if (length > 1) {
0400: digits.insert(1, '.');
0401: }
0402: if (mantissa != 0) {
0403: digits.append("E" + mantissa);
0404: }
0405: } else if (mantissa <= -1) {
0406: digits.insert(0, "0.");
0407: for (int i = mantissa; i < -1; i++) {
0408: digits.insert(2, '0');
0409: }
0410: } else if (mantissa + 1 == length) {
0411: if (length > 1 && digits.charAt(digits.length() - 1) == '0') {
0412: digits.append('.');
0413: }
0414: } else if (mantissa < length) {
0415: digits.insert(mantissa + 1, '.');
0416: } else {
0417: for (int i = length; i <= mantissa; i++) {
0418: digits.append('0');
0419: }
0420: }
0421: if (!sign) {
0422: digits.insert(0, '-');
0423: }
0424: return digits.toString();
0425: }
0426:
0427: /**
0428: * Formats this number in scientific notation.
0429: * <p>
0430: * A string such as "NaN" or "Infinity" may be returned by this method.
0431: *
0432: * @return representation of this number in scientific notation.
0433: *
0434: * @since ostermillerutils 1.00.00
0435: */
0436: public String toScientificNotation() {
0437: if (digits == null)
0438: return original;
0439: StringBuffer digits = new StringBuffer(this .digits.toString());
0440: int length = digits.length();
0441: if (length > 1) {
0442: digits.insert(1, '.');
0443: }
0444: if (mantissa != 0) {
0445: digits.append("E" + mantissa);
0446: }
0447: if (!sign) {
0448: digits.insert(0, '-');
0449: }
0450: return digits.toString();
0451: }
0452:
0453: /**
0454: * Parsing state:
0455: * Initial state before anything read.
0456: *
0457: * @since ostermillerutils 1.00.00
0458: */
0459: private final static int INITIAL = 0;
0460: /**
0461: * Parsing state:
0462: * State in which a possible sign and
0463: * possible leading zeros have been read.
0464: *
0465: * @since ostermillerutils 1.00.00
0466: */
0467: private final static int LEADZEROS = 1;
0468: /**
0469: * Parsing state:
0470: * State in which a possible sign and
0471: * at least one non-zero digit
0472: * has been read followed by some number of
0473: * zeros. The decimal place has no
0474: * been encountered yet.
0475: *
0476: * @since ostermillerutils 1.00.00
0477: */
0478: private final static int MIDZEROS = 2;
0479: /**
0480: * Parsing state:
0481: * State in which a possible sign and
0482: * at least one non-zero digit
0483: * has been read. The decimal place has no
0484: * been encountered yet.
0485: *
0486: * @since ostermillerutils 1.00.00
0487: */
0488: private final static int DIGITS = 3;
0489: /**
0490: * Parsing state:
0491: * State in which only a possible sign,
0492: * leading zeros, and a decimal point
0493: * have been encountered.
0494: *
0495: * @since ostermillerutils 1.00.00
0496: */
0497: private final static int LEADZEROSDOT = 4;
0498: /**
0499: * Parsing state:
0500: * State in which a possible sign,
0501: * at least one nonzero digit and a
0502: * decimal point have been encountered.
0503: *
0504: * @since ostermillerutils 1.00.00
0505: */
0506: private final static int DIGITSDOT = 5;
0507: /**
0508: * Parsing state:
0509: * State in which the exponent symbol
0510: * 'E' has been encountered.
0511: *
0512: * @since ostermillerutils 1.00.00
0513: */
0514: private final static int MANTISSA = 6;
0515: /**
0516: * Parsing state:
0517: * State in which the exponent symbol
0518: * 'E' has been encountered followed
0519: * by a possible sign or some number
0520: * of digits.
0521: *
0522: * @since ostermillerutils 1.00.00
0523: */
0524: private final static int MANTISSADIGIT = 7;
0525:
0526: /**
0527: * Parse a number from the given string.
0528: * A valid number has an optional sign, some digits
0529: * with an optional decimal point, and an optional
0530: * scientific notation part consisting of an 'E' followed
0531: * by an optional sign, followed by some digits.
0532: *
0533: * @param number String representation of a number.
0534: * @throws NumberFormatException if the string is not a valid number.
0535: *
0536: * @since ostermillerutils 1.00.00
0537: */
0538: private void parse(String number) throws NumberFormatException {
0539: int length = number.length();
0540: digits = new StringBuffer(length);
0541: int state = INITIAL;
0542: int mantissaStart = -1;
0543: boolean foundMantissaDigit = false;
0544: // sometimes we don't know if a zero will be
0545: // significant or not when it is encountered.
0546: // keep track of the number of them so that
0547: // the all can be made significant if we find
0548: // out that they are.
0549: int zeroCount = 0;
0550: int leadZeroCount = 0;
0551:
0552: for (int i = 0; i < length; i++) {
0553: char c = number.charAt(i);
0554: switch (c) {
0555: case '.': {
0556: switch (state) {
0557: case INITIAL:
0558: case LEADZEROS: {
0559: state = LEADZEROSDOT;
0560: }
0561: break;
0562: case MIDZEROS: {
0563: // we now know that these zeros
0564: // are more than just trailing place holders.
0565: for (int j = 0; j < zeroCount; j++) {
0566: digits.append('0');
0567: }
0568: zeroCount = 0;
0569: state = DIGITSDOT;
0570: }
0571: break;
0572: case DIGITS: {
0573: state = DIGITSDOT;
0574: }
0575: break;
0576: default: {
0577: throw new NumberFormatException(
0578: "Unexpected character '" + c
0579: + "' at position " + i);
0580: }
0581: }
0582: }
0583: break;
0584: case '+': {
0585: switch (state) {
0586: case INITIAL: {
0587: sign = true;
0588: state = LEADZEROS;
0589: }
0590: break;
0591: case MANTISSA: {
0592: state = MANTISSADIGIT;
0593: }
0594: break;
0595: default: {
0596: throw new NumberFormatException(
0597: "Unexpected character '" + c
0598: + "' at position " + i);
0599: }
0600: }
0601: }
0602: break;
0603: case '-': {
0604: switch (state) {
0605: case INITIAL: {
0606: sign = false;
0607: state = LEADZEROS;
0608: }
0609: break;
0610: case MANTISSA: {
0611: state = MANTISSADIGIT;
0612: }
0613: break;
0614: default: {
0615: throw new NumberFormatException(
0616: "Unexpected character '" + c
0617: + "' at position " + i);
0618: }
0619: }
0620: }
0621: break;
0622: case '0': {
0623: switch (state) {
0624: case INITIAL:
0625: case LEADZEROS: {
0626: // only significant if number
0627: // is all zeros.
0628: zeroCount++;
0629: leadZeroCount++;
0630: state = LEADZEROS;
0631: }
0632: break;
0633: case MIDZEROS:
0634: case DIGITS: {
0635: // only significant if followed
0636: // by a decimal point or nonzero digit.
0637: mantissa++;
0638: zeroCount++;
0639: state = MIDZEROS;
0640: }
0641: break;
0642: case LEADZEROSDOT: {
0643: // only significant if number
0644: // is all zeros.
0645: mantissa--;
0646: zeroCount++;
0647: state = LEADZEROSDOT;
0648: }
0649: break;
0650: case DIGITSDOT: {
0651: // non-leading zeros after
0652: // a decimal point are always
0653: // significant.
0654: digits.append(c);
0655: }
0656: break;
0657: case MANTISSA:
0658: case MANTISSADIGIT: {
0659: foundMantissaDigit = true;
0660: state = MANTISSADIGIT;
0661: }
0662: break;
0663: default: {
0664: throw new NumberFormatException(
0665: "Unexpected character '" + c
0666: + "' at position " + i);
0667: }
0668: }
0669: }
0670: break;
0671: case '1':
0672: case '2':
0673: case '3':
0674: case '4':
0675: case '5':
0676: case '6':
0677: case '7':
0678: case '8':
0679: case '9': {
0680: switch (state) {
0681: case INITIAL:
0682: case LEADZEROS:
0683: case DIGITS: {
0684: zeroCount = 0;
0685: digits.append(c);
0686: mantissa++;
0687: state = DIGITS;
0688: }
0689: break;
0690: case MIDZEROS: {
0691: // we now know that these zeros
0692: // are more than just trailing place holders.
0693: for (int j = 0; j < zeroCount; j++) {
0694: digits.append('0');
0695: }
0696: zeroCount = 0;
0697: digits.append(c);
0698: mantissa++;
0699: state = DIGITS;
0700: }
0701: break;
0702: case LEADZEROSDOT:
0703: case DIGITSDOT: {
0704: zeroCount = 0;
0705: digits.append(c);
0706: state = DIGITSDOT;
0707: }
0708: break;
0709: case MANTISSA:
0710: case MANTISSADIGIT: {
0711: state = MANTISSADIGIT;
0712: foundMantissaDigit = true;
0713: }
0714: break;
0715: default: {
0716: throw new NumberFormatException(
0717: "Unexpected character '" + c
0718: + "' at position " + i);
0719: }
0720: }
0721: }
0722: break;
0723: case 'E':
0724: case 'e': {
0725: switch (state) {
0726: case INITIAL:
0727: case LEADZEROS:
0728: case DIGITS:
0729: case LEADZEROSDOT:
0730: case DIGITSDOT: {
0731: // record the starting point of the mantissa
0732: // so we can do a substring to get it back later
0733: mantissaStart = i + 1;
0734: state = MANTISSA;
0735: }
0736: break;
0737: default: {
0738: throw new NumberFormatException(
0739: "Unexpected character '" + c
0740: + "' at position " + i);
0741: }
0742: }
0743: }
0744: break;
0745: default: {
0746: throw new NumberFormatException(
0747: "Unexpected character '" + c + "' at position "
0748: + i);
0749: }
0750: }
0751: }
0752: if (mantissaStart != -1) {
0753: // if we had found an 'E'
0754: if (!foundMantissaDigit) {
0755: // we didn't actually find a mantissa to go with.
0756: throw new NumberFormatException(
0757: "No digits in mantissa.");
0758: }
0759: // parse the mantissa.
0760: mantissa += Integer.parseInt(number
0761: .substring(mantissaStart));
0762: }
0763: if (digits.length() == 0) {
0764: if (zeroCount > 0) {
0765: // if nothing but zeros all zeros are significant.
0766: for (int j = 0; j < zeroCount; j++) {
0767: digits.append('0');
0768: }
0769: mantissa += leadZeroCount;
0770: isZero = true;
0771: sign = true;
0772: } else {
0773: // a hack to catch some cases that we could catch
0774: // by adding a ton of extra states. Things like:
0775: // "e2" "+e2" "+." "." "+" etc.
0776: throw new NumberFormatException("No digits in number.");
0777: }
0778: }
0779: }
0780:
0781: /**
0782: * Adjust the number of digits in the number.
0783: * Pad the tail with zeros if too short, round the
0784: * number according to scientific rounding if too long, leave alone
0785: * if just right.
0786: * <p>
0787: * This method has no effect if this number is not a number or infinity.
0788: *
0789: * @param significantFigures desired number of significant figures.
0790: * @return This number.
0791: *
0792: * @since ostermillerutils 1.00.00
0793: */
0794: public SignificantFigures setNumberSignificantFigures(
0795: int significantFigures) {
0796: if (significantFigures <= 0)
0797: throw new IllegalArgumentException(
0798: "Desired number of significant figures must be positive.");
0799: if (digits != null) {
0800: int length = digits.length();
0801: if (length < significantFigures) {
0802: // number is not long enough, pad it with zeros.
0803: for (int i = length; i < significantFigures; i++) {
0804: digits.append('0');
0805: }
0806: } else if (length > significantFigures) {
0807: // number is too long chop some of it off with rounding.
0808: boolean addOne; // we need to round up if true.
0809: char firstInSig = digits.charAt(significantFigures);
0810: if (firstInSig < '5') {
0811: // first non-significant digit less than five, round down.
0812: addOne = false;
0813: } else if (firstInSig == '5') {
0814: // first non-significant digit equal to five
0815: addOne = false;
0816: for (int i = significantFigures + 1; !addOne
0817: && i < length; i++) {
0818: // if its followed by any non-zero digits, round up.
0819: if (digits.charAt(i) != '0') {
0820: addOne = true;
0821: }
0822: }
0823: if (!addOne) {
0824: // if it was not followed by non-zero digits
0825: // if the last significant digit is odd round up
0826: // if the last significant digit is even round down
0827: addOne = (digits.charAt(significantFigures - 1) & 1) == 1;
0828: }
0829: } else {
0830: // first non-significant digit greater than five, round up.
0831: addOne = true;
0832: }
0833: // loop to add one (and carry a one if added to a nine)
0834: // to the last significant digit
0835: for (int i = significantFigures - 1; addOne && i >= 0; i--) {
0836: char digit = digits.charAt(i);
0837: if (digit < '9') {
0838: digits.setCharAt(i, (char) (digit + 1));
0839: addOne = false;
0840: } else {
0841: digits.setCharAt(i, '0');
0842: }
0843: }
0844: if (addOne) {
0845: // if the number was all nines
0846: digits.insert(0, '1');
0847: mantissa++;
0848: }
0849: // chop it to the correct number of figures.
0850: digits.setLength(significantFigures);
0851: }
0852: }
0853: return this ;
0854: }
0855:
0856: /**
0857: * Returns the value of this number as a byte.
0858: *
0859: * @return the numeric value represented by this object after conversion to type byte.
0860: * @throws NumberFormatException if this number cannot be converted to a byte.
0861: *
0862: * @since ostermillerutils 1.00.00
0863: */
0864: @Override
0865: public byte byteValue() throws NumberFormatException {
0866: return Byte.parseByte(original);
0867: }
0868:
0869: /**
0870: * Returns the value of this number as a double.
0871: *
0872: * @return the numeric value represented by this object after conversion to type double.
0873: * @throws NumberFormatException if this number cannot be converted to a double.
0874: *
0875: * @since ostermillerutils 1.00.00
0876: */
0877: @Override
0878: public double doubleValue() throws NumberFormatException {
0879: return Double.parseDouble(original);
0880: }
0881:
0882: /**
0883: * Returns the value of this number as a float.
0884: *
0885: * @return the numeric value represented by this object after conversion to type float.
0886: * @throws NumberFormatException if this number cannot be converted to a float.
0887: *
0888: * @since ostermillerutils 1.00.00
0889: */
0890: @Override
0891: public float floatValue() throws NumberFormatException {
0892: return Float.parseFloat(original);
0893: }
0894:
0895: /**
0896: * Returns the value of this number as a int.
0897: *
0898: * @return the numeric value represented by this object after conversion to type int.
0899: * @throws NumberFormatException if this number cannot be converted to a int.
0900: *
0901: * @since ostermillerutils 1.00.00
0902: */
0903: @Override
0904: public int intValue() throws NumberFormatException {
0905: return Integer.parseInt(original);
0906: }
0907:
0908: /**
0909: * Returns the value of this number as a long.
0910: *
0911: * @return the numeric value represented by this object after conversion to type long.
0912: * @throws NumberFormatException if this number cannot be converted to a long.
0913: *
0914: * @since ostermillerutils 1.00.00
0915: */
0916: @Override
0917: public long longValue() throws NumberFormatException {
0918: return Long.parseLong(original);
0919: }
0920:
0921: /**
0922: * Returns the value of this number as a short.
0923: *
0924: * @return the numeric value represented by this object after conversion to type short.
0925: * @throws NumberFormatException if this number cannot be converted to a short.
0926: *
0927: * @since ostermillerutils 1.00.00
0928: */
0929: @Override
0930: public short shortValue() throws NumberFormatException {
0931: return Short.parseShort(original);
0932: }
0933:
0934: /**
0935: * Convenience method to display a number with the correct
0936: * significant digits.
0937: *
0938: * @param number the number to display
0939: * @param significantFigures the number of significant figures to display.
0940: * @return the number formatted with the correct significant figures
0941: *
0942: * @since ostermillerutils 1.02.07
0943: */
0944: public static String format(byte number, int significantFigures) {
0945: SignificantFigures sf = new SignificantFigures(number);
0946: sf.setNumberSignificantFigures(significantFigures);
0947: return sf.toString();
0948: }
0949:
0950: /**
0951: * Convenience method to display a number with the correct
0952: * significant digits.
0953: *
0954: * @param number the number to display
0955: * @param significantFigures the number of significant figures to display.
0956: * @return the number formatted with the correct significant figures
0957: *
0958: * @since ostermillerutils 1.02.07
0959: */
0960: public static String format(double number, int significantFigures) {
0961: SignificantFigures sf = new SignificantFigures(number);
0962: sf.setNumberSignificantFigures(significantFigures);
0963: return sf.toString();
0964: }
0965:
0966: /**
0967: * Convenience method to display a number with the correct
0968: * significant digits.
0969: *
0970: * @param number the number to display
0971: * @param significantFigures the number of significant figures to display.
0972: * @return the number formatted with the correct significant figures
0973: *
0974: * @since ostermillerutils 1.02.07
0975: */
0976: public static String format(float number, int significantFigures) {
0977: SignificantFigures sf = new SignificantFigures(number);
0978: sf.setNumberSignificantFigures(significantFigures);
0979: return sf.toString();
0980: }
0981:
0982: /**
0983: * Convenience method to display a number with the correct
0984: * significant digits.
0985: *
0986: * @param number the number to display
0987: * @param significantFigures the number of significant figures to display.
0988: * @return the number formatted with the correct significant figures
0989: *
0990: * @since ostermillerutils 1.02.07
0991: */
0992: public static String format(int number, int significantFigures) {
0993: SignificantFigures sf = new SignificantFigures(number);
0994: sf.setNumberSignificantFigures(significantFigures);
0995: return sf.toString();
0996: }
0997:
0998: /**
0999: * Convenience method to display a number with the correct
1000: * significant digits.
1001: *
1002: * @param number the number to display
1003: * @param significantFigures the number of significant figures to display.
1004: * @return the number formatted with the correct significant figures
1005: *
1006: * @since ostermillerutils 1.02.07
1007: */
1008: public static String format(long number, int significantFigures) {
1009: SignificantFigures sf = new SignificantFigures(number);
1010: sf.setNumberSignificantFigures(significantFigures);
1011: return sf.toString();
1012: }
1013:
1014: /**
1015: * Convenience method to display a number with the correct
1016: * significant digits.
1017: *
1018: * @param number the number to display
1019: * @param significantFigures the number of significant figures to display.
1020: * @return the number formatted with the correct significant figures
1021: *
1022: * @since ostermillerutils 1.02.07
1023: */
1024: public static String format(Number number, int significantFigures) {
1025: SignificantFigures sf = new SignificantFigures(number);
1026: sf.setNumberSignificantFigures(significantFigures);
1027: return sf.toString();
1028: }
1029:
1030: /**
1031: * Convenience method to display a number with the correct
1032: * significant digits.
1033: *
1034: * @param number the number to display
1035: * @param significantFigures the number of significant figures to display.
1036: * @return the number formatted with the correct significant figures
1037: *
1038: * @since ostermillerutils 1.02.07
1039: */
1040: public static String format(short number, int significantFigures) {
1041: SignificantFigures sf = new SignificantFigures(number);
1042: sf.setNumberSignificantFigures(significantFigures);
1043: return sf.toString();
1044: }
1045:
1046: /**
1047: * Convenience method to display a number with the correct
1048: * significant digits.
1049: *
1050: * @param number the number to display
1051: * @param significantFigures the number of significant figures to display.
1052: * @return the number formatted with the correct significant figures
1053: * @throws NumberFormatException if the String is not a valid number.
1054: *
1055: * @since ostermillerutils 1.02.07
1056: */
1057: public static String format(String number, int significantFigures)
1058: throws NumberFormatException {
1059: SignificantFigures sf = new SignificantFigures(number);
1060: sf.setNumberSignificantFigures(significantFigures);
1061: return sf.toString();
1062: }
1063: }
|