/** * Converts a string representation of a real number into a DoubleDouble value. The format * accepted is similar to the standard Java real number syntax. It is defined by the following * regular expression: * * <pre> * [<tt>+</tt>|<tt>-</tt>] {<i>digit</i>} [ <tt>.</tt> {<i>digit</i>} ] [ ( <tt>e</tt> | <tt>E</tt> ) [<tt>+</tt>|<tt>-</tt> * ] {<i>digit</i>}+ * * </pre> * * @param str the string to parse * @return the value of the parsed number * @throws NumberFormatException if <tt>str</tt> is not a valid representation of a number */ public static WB_DoubleDouble parse(final String str) throws NumberFormatException { int i = 0; final int strlen = str.length(); // skip leading whitespace while (Character.isWhitespace(str.charAt(i))) { i++; } // check for sign boolean isNegative = false; if (i < strlen) { final char signCh = str.charAt(i); if ((signCh == '-') || (signCh == '+')) { i++; if (signCh == '-') { isNegative = true; } } } // scan all digits and accumulate into an integral value // Keep track of the location of the decimal point (if any) to allow // scaling later final WB_DoubleDouble val = new WB_DoubleDouble(); int numDigits = 0; int numBeforeDec = 0; int exp = 0; while (true) { if (i >= strlen) { break; } final char ch = str.charAt(i); i++; if (Character.isDigit(ch)) { final double d = ch - '0'; val.selfMultiply(TEN); // MD: need to optimize this val.selfAdd(d); numDigits++; continue; } if (ch == '.') { numBeforeDec = numDigits; continue; } if ((ch == 'e') || (ch == 'E')) { final String expStr = str.substring(i); // this should catch any format problems with the exponent try { exp = Integer.parseInt(expStr); } catch (final NumberFormatException ex) { throw new NumberFormatException("Invalid exponent " + expStr + " in string " + str); } break; } throw new NumberFormatException( "Unexpected character '" + ch + "' at position " + i + " in string " + str); } WB_DoubleDouble val2 = val; // scale the number correctly final int numDecPlaces = numDigits - numBeforeDec - exp; if (numDecPlaces == 0) { val2 = val; } else if (numDecPlaces > 0) { final WB_DoubleDouble scale = TEN.pow(numDecPlaces); val2 = val.divide(scale); } else if (numDecPlaces < 0) { final WB_DoubleDouble scale = TEN.pow(-numDecPlaces); val2 = val.multiply(scale); } // apply leading sign, if any if (isNegative) { return val2.negate(); } return val2; }
/** * Extracts the significant digits in the decimal representation of the argument. A decimal point * may be optionally inserted in the string of digits (as long as its position lies within the * extracted digits - if not, the caller must prepend or append the appropriate zeroes and decimal * point). * * @param insertDecimalPoint the insert decimal point * @param magnitude the magnitude * @return the string containing the significant digits and possibly a decimal point */ private String extractSignificantDigits(final boolean insertDecimalPoint, final int[] magnitude) { WB_DoubleDouble y = this.abs(); // compute *correct* magnitude of y int mag = magnitude(y.hi); final WB_DoubleDouble scale = TEN.pow(mag); y = y.divide(scale); // fix magnitude if off by one if (y.gt(TEN)) { y = y.divide(TEN); mag += 1; } else if (y.lt(ONE)) { y = y.multiply(TEN); mag -= 1; } final int decimalPointPos = mag + 1; final StringBuffer buf = new StringBuffer(); final int numDigits = MAX_PRINT_DIGITS - 1; for (int i = 0; i <= numDigits; i++) { if (insertDecimalPoint && (i == decimalPointPos)) { buf.append('.'); } final int digit = (int) y.hi; // System.out.println("printDump: [" + i + "] digit: " + digit + // " y: " + y.dump() + " buf: " + buf); /** This should never happen, due to heuristic checks on remainder below */ if ((digit < 0) || (digit > 9)) { // System.out.println("digit > 10 : " + digit); // throw new // IllegalStateException("Internal errror: found digit = " + // digit); } /** * If a negative remainder is encountered, simply terminate the extraction. This is robust, * but maybe slightly inaccurate. My current hypothesis is that negative remainders only occur * for very small lo components, so the inaccuracy is tolerable */ if (digit < 0) { break; // throw new // IllegalStateException("Internal errror: found digit = " + // digit); } boolean rebiasBy10 = false; char digitChar = 0; if (digit > 9) { // set flag to re-bias after next 10-shift rebiasBy10 = true; // output digit will end up being '9' digitChar = '9'; } else { digitChar = (char) ('0' + digit); } buf.append(digitChar); y = (y.subtract(WB_DoubleDouble.valueOf(digit)).multiply(TEN)); if (rebiasBy10) { y.selfAdd(TEN); } boolean continueExtractingDigits = true; /** * Heuristic check: if the remaining portion of y is non-positive, assume that output is * complete */ // if (y.hi <= 0.0) // if (y.hi < 0.0) // continueExtractingDigits = false; /** * Check if remaining digits will be 0, and if so don't output them. Do this by comparing the * magnitude of the remainder with the expected precision. */ final int remMag = magnitude(y.hi); if ((remMag < 0) && (Math.abs(remMag) >= (numDigits - i))) { continueExtractingDigits = false; } if (!continueExtractingDigits) { break; } } magnitude[0] = mag; return buf.toString(); }