All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vividsolutions.jts.math.DD Maven / Gradle / Ivy

There is a newer version: 0.1.4
Show newest version
/*
 * The JTS Topology Suite is a collection of Java classes that
 * implement the fundamental operations required to validate a given
 * geo-spatial data set to a known topological specification.
 *
 * Copyright (C) 2001 Vivid Solutions
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * For more information, contact:
 *
 *     Vivid Solutions
 *     Suite #1A
 *     2328 Government Street
 *     Victoria BC  V8T 5G5
 *     Canada
 *
 *     (250)385-6040
 *     www.vividsolutions.com
 */
package com.vividsolutions.jts.math;

import java.io.*;

/**
 * Implements extended-precision floating-point numbers 
 * which maintain 106 bits (approximately 30 decimal digits) of precision. 
 * 

* A DoubleDouble uses a representation containing two double-precision values. * A number x is represented as a pair of doubles, x.hi and x.lo, * such that the number represented by x is x.hi + x.lo, where *

 *    |x.lo| <= 0.5*ulp(x.hi)
 * 
* and ulp(y) means "unit in the last place of y". * The basic arithmetic operations are implemented using * convenient properties of IEEE-754 floating-point arithmetic. *

* The range of values which can be represented is the same as in IEEE-754. * The precision of the representable numbers * is twice as great as IEEE-754 double precision. *

* The correctness of the arithmetic algorithms relies on operations * being performed with standard IEEE-754 double precision and rounding. * This is the Java standard arithmetic model, but for performance reasons * Java implementations are not * constrained to using this standard by default. * Some processors (notably the Intel Pentium architecure) perform * floating point operations in (non-IEEE-754-standard) extended-precision. * A JVM implementation may choose to use the non-standard extended-precision * as its default arithmetic mode. * To prevent this from happening, this code uses the * Java strictfp modifier, * which forces all operations to take place in the standard IEEE-754 rounding model. *

* The API provides both a set of value-oriented operations * and a set of mutating operations. * Value-oriented operations treat DoubleDouble values as * immutable; operations on them return new objects carrying the result * of the operation. This provides a simple and safe semantics for * writing DoubleDouble expressions. However, there is a performance * penalty for the object allocations required. * The mutable interface updates object values in-place. * It provides optimum memory performance, but requires * care to ensure that aliasing errors are not created * and constant values are not changed. *

* For example, the following code example constructs three DD instances: * two to hold the input values and one to hold the result of the addition. *

 *     DD a = new DD(2.0);
 *     DD b = new DD(3.0);
 *     DD c = a.add(b);
 * 
* In contrast, the following approach uses only one object: *
 *     DD a = new DD(2.0);
 *     a.selfAdd(3.0);
 * 
*

* This implementation uses algorithms originally designed variously by * Knuth, Kahan, Dekker, and Linnainmaa. * Douglas Priest developed the first C implementation of these techniques. * Other more recent C++ implementation are due to Keith M. Briggs and David Bailey et al. * *

References

*
    *
  • Priest, D., Algorithms for Arbitrary Precision Floating Point Arithmetic, * in P. Kornerup and D. Matula, Eds., Proc. 10th Symposium on Computer Arithmetic, * IEEE Computer Society Press, Los Alamitos, Calif., 1991. *
  • Yozo Hida, Xiaoye S. Li and David H. Bailey, * Quad-Double Arithmetic: Algorithms, Implementation, and Application, * manuscript, Oct 2000; Lawrence Berkeley National Laboratory Report BNL-46996. *
  • David Bailey, High Precision Software Directory; * http://crd.lbl.gov/~dhbailey/mpdist/index.html *
* * * @author Martin Davis * */ public strictfp final class DD implements Serializable, Comparable, Cloneable { /** * The value nearest to the constant Pi. */ public static final DD PI = new DD( 3.141592653589793116e+00, 1.224646799147353207e-16); /** * The value nearest to the constant 2 * Pi. */ public static final DD TWO_PI = new DD( 6.283185307179586232e+00, 2.449293598294706414e-16); /** * The value nearest to the constant Pi / 2. */ public static final DD PI_2 = new DD( 1.570796326794896558e+00, 6.123233995736766036e-17); /** * The value nearest to the constant e (the natural logarithm base). */ public static final DD E = new DD( 2.718281828459045091e+00, 1.445646891729250158e-16); /** * A value representing the result of an operation which does not return a valid number. */ public static final DD NaN = new DD(Double.NaN, Double.NaN); /** * The smallest representable relative difference between two {link @ DoubleDouble} values */ public static final double EPS = 1.23259516440783e-32; /* = 2^-106 */ private static DD createNaN() { return new DD(Double.NaN, Double.NaN); } /** * Converts the string argument to a DoubleDouble number. * * @param str a string containing a representation of a numeric value * @return the extended precision version of the value * @throws NumberFormatException if s is not a valid representation of a number */ public static DD valueOf(String str) throws NumberFormatException { return parse(str); } /** * Converts the double argument to a DoubleDouble number. * * @param x a numeric value * @return the extended precision version of the value */ public static DD valueOf(double x) { return new DD(x); } /** * The value to split a double-precision value on during multiplication */ private static final double SPLIT = 134217729.0D; // 2^27+1, for IEEE double /** * The high-order component of the double-double precision value. */ private double hi = 0.0; /** * The low-order component of the double-double precision value. */ private double lo = 0.0; /** * Creates a new DoubleDouble with value 0.0. */ public DD() { init(0.0); } /** * Creates a new DoubleDouble with value x. * * @param x the value to initialize */ public DD(double x) { init(x); } /** * Creates a new DoubleDouble with value (hi, lo). * * @param hi the high-order component * @param lo the high-order component */ public DD(double hi, double lo) { init(hi, lo); } /** * Creates a new DoubleDouble with value equal to the argument. * * @param dd the value to initialize */ public DD(DD dd) { init(dd); } /** * Creates a new DoubleDouble with value equal to the argument. * * @param str the value to initialize by * @throws NumberFormatException if str is not a valid representation of a number */ public DD(String str) throws NumberFormatException { this(parse(str)); } /** * Creates a new DoubleDouble with the value of the argument. * * @param dd the DoubleDouble value to copy * @return a copy of the input value */ public static DD copy(DD dd) { return new DD(dd); } /** * Creates and returns a copy of this value. * * @return a copy of this value */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException ex) { // should never reach here return null; } } private final void init(double x) { this.hi = x; this.lo = 0.0; } private final void init(double hi, double lo) { this.hi = hi; this.lo = lo; } private final void init(DD dd) { hi = dd.hi; lo = dd.lo; } /* double getHighComponent() { return hi; } double getLowComponent() { return lo; } */ // Testing only - should not be public /* public void RENORM() { double s = hi + lo; double err = lo - (s - hi); hi = s; lo = err; } */ /** * Set the value for the DD object. This method supports the mutating * operations concept described in the class documentation (see above). * @param value a DD instance supplying an extended-precision value. * @return a self-reference to the DD instance. */ public DD setValue(DD value) { init(value); return this; } /** * Set the value for the DD object. This method supports the mutating * operations concept described in the class documentation (see above). * @param value a floating point value to be stored in the instance. * @return a self-reference to the DD instance. */ public DD setValue(double value) { init(value); return this; } /** * Returns a new DoubleDouble whose value is (this + y). * * @param y the addend * @return (this + y) */ public final DD add(DD y) { return copy(this).selfAdd(y); } /** * Returns a new DoubleDouble whose value is (this + y). * * @param y the addend * @return (this + y) */ public final DD add(double y) { return copy(this).selfAdd(y); } /** * Adds the argument to the value of this. * To prevent altering constants, * this method must only be used on values known to * be newly created. * * @param y the addend * @return this object, increased by y */ public final DD selfAdd(DD y) { return selfAdd(y.hi, y.lo); } /** * Adds the argument to the value of this. * To prevent altering constants, * this method must only be used on values known to * be newly created. * * @param y the addend * @return this object, increased by y */ public final DD selfAdd(double y) { double H, h, S, s, e, f; S = hi + y; e = S - hi; s = S - e; s = (y - e) + (hi - s); f = s + lo; H = S + f; h = f + (S - H); hi = H + h; lo = h + (H - hi); return this; // return selfAdd(y, 0.0); } private final DD selfAdd(double yhi, double ylo) { double H, h, T, t, S, s, e, f; S = hi + yhi; T = lo + ylo; e = S - hi; f = T - lo; s = S-e; t = T-f; s = (yhi-e)+(hi-s); t = (ylo-f)+(lo-t); e = s+T; H = S+e; h = e+(S-H); e = t+h; double zhi = H + e; double zlo = e + (H - zhi); hi = zhi; lo = zlo; return this; } /** * Computes a new DoubleDouble object whose value is (this - y). * * @param y the subtrahend * @return (this - y) */ public final DD subtract(DD y) { return add(y.negate()); } /** * Computes a new DoubleDouble object whose value is (this - y). * * @param y the subtrahend * @return (this - y) */ public final DD subtract(double y) { return add(-y); } /** * Subtracts the argument from the value of this. * To prevent altering constants, * this method must only be used on values known to * be newly created. * * @param y the addend * @return this object, decreased by y */ public final DD selfSubtract(DD y) { if (isNaN()) return this; return selfAdd(-y.hi, -y.lo); } /** * Subtracts the argument from the value of this. * To prevent altering constants, * this method must only be used on values known to * be newly created. * * @param y the addend * @return this object, decreased by y */ public final DD selfSubtract(double y) { if (isNaN()) return this; return selfAdd(-y, 0.0); } /** * Returns a new DoubleDouble whose value is -this. * * @return -this */ public final DD negate() { if (isNaN()) return this; return new DD(-hi, -lo); } /** * Returns a new DoubleDouble whose value is (this * y). * * @param y the multiplicand * @return (this * y) */ public final DD multiply(DD y) { if (y.isNaN()) return createNaN(); return copy(this).selfMultiply(y); } /** * Returns a new DoubleDouble whose value is (this * y). * * @param y the multiplicand * @return (this * y) */ public final DD multiply(double y) { if (Double.isNaN(y)) return createNaN(); return copy(this).selfMultiply(y, 0.0); } /** * Multiplies this object by the argument, returning this. * To prevent altering constants, * this method must only be used on values known to * be newly created. * * @param y the value to multiply by * @return this object, multiplied by y */ public final DD selfMultiply(DD y) { return selfMultiply(y.hi, y.lo); } /** * Multiplies this object by the argument, returning this. * To prevent altering constants, * this method must only be used on values known to * be newly created. * * @param y the value to multiply by * @return this object, multiplied by y */ public final DD selfMultiply(double y) { return selfMultiply(y, 0.0); } private final DD selfMultiply(double yhi, double ylo) { double hx, tx, hy, ty, C, c; C = SPLIT * hi; hx = C-hi; c = SPLIT * yhi; hx = C-hx; tx = hi-hx; hy = c-yhi; C = hi*yhi; hy = c-hy; ty = yhi-hy; c = ((((hx*hy-C)+hx*ty)+tx*hy)+tx*ty)+(hi*ylo+lo*yhi); double zhi = C+c; hx = C-zhi; double zlo = c+hx; hi = zhi; lo = zlo; return this; } /** * Computes a new DoubleDouble whose value is (this / y). * * @param y the divisor * @return a new object with the value (this / y) */ public final DD divide(DD y) { double hc, tc, hy, ty, C, c, U, u; C = hi/y.hi; c = SPLIT*C; hc =c-C; u = SPLIT*y.hi; hc = c-hc; tc = C-hc; hy = u-y.hi; U = C * y.hi; hy = u-hy; ty = y.hi-hy; u = (((hc*hy-U)+hc*ty)+tc*hy)+tc*ty; c = ((((hi-U)-u)+lo)-C*y.lo)/y.hi; u = C+c; double zhi = u; double zlo = (C-u)+c; return new DD(zhi, zlo); } /** * Computes a new DoubleDouble whose value is (this / y). * * @param y the divisor * @return a new object with the value (this / y) */ public final DD divide(double y) { if (Double.isNaN(y)) return createNaN(); return copy(this).selfDivide(y, 0.0); } /** * Divides this object by the argument, returning this. * To prevent altering constants, * this method must only be used on values known to * be newly created. * * @param y the value to divide by * @return this object, divided by y */ public final DD selfDivide(DD y) { return selfDivide(y.hi, y.lo); } /** * Divides this object by the argument, returning this. * To prevent altering constants, * this method must only be used on values known to * be newly created. * * @param y the value to divide by * @return this object, divided by y */ public final DD selfDivide(double y) { return selfDivide(y, 0.0); } private final DD selfDivide(double yhi, double ylo) { double hc, tc, hy, ty, C, c, U, u; C = hi/yhi; c = SPLIT*C; hc =c-C; u = SPLIT*yhi; hc = c-hc; tc = C-hc; hy = u-yhi; U = C * yhi; hy = u-hy; ty = yhi-hy; u = (((hc*hy-U)+hc*ty)+tc*hy)+tc*ty; c = ((((hi-U)-u)+lo)-C*ylo)/yhi; u = C+c; hi = u; lo = (C-u)+c; return this; } /** * Returns a DoubleDouble whose value is 1 / this. * * @return the reciprocal of this value */ public final DD reciprocal() { double hc, tc, hy, ty, C, c, U, u; C = 1.0/hi; c = SPLIT*C; hc =c-C; u = SPLIT*hi; hc = c-hc; tc = C-hc; hy = u-hi; U = C*hi; hy = u-hy; ty = hi-hy; u = (((hc*hy-U)+hc*ty)+tc*hy)+tc*ty; c = ((((1.0-U)-u))-C*lo)/hi; double zhi = C+c; double zlo = (C-zhi)+c; return new DD(zhi, zlo); } /** * Returns the largest (closest to positive infinity) * value that is not greater than the argument * and is equal to a mathematical integer. * Special cases: *
    *
  • If this value is NaN, returns NaN. *
* * @return the largest (closest to positive infinity) * value that is not greater than the argument * and is equal to a mathematical integer. */ public DD floor() { if (isNaN()) return NaN; double fhi=Math.floor(hi); double flo = 0.0; // Hi is already integral. Floor the low word if (fhi == hi) { flo = Math.floor(lo); } // do we need to renormalize here? return new DD(fhi, flo); } /** * Returns the smallest (closest to negative infinity) value * that is not less than the argument and is equal to a mathematical integer. * Special cases: *
    *
  • If this value is NaN, returns NaN. *
* * @return the smallest (closest to negative infinity) value * that is not less than the argument and is equal to a mathematical integer. */ public DD ceil() { if (isNaN()) return NaN; double fhi=Math.ceil(hi); double flo = 0.0; // Hi is already integral. Ceil the low word if (fhi == hi) { flo = Math.ceil(lo); // do we need to renormalize here? } return new DD(fhi, flo); } /** * Returns an integer indicating the sign of this value. *
    *
  • if this value is > 0, returns 1 *
  • if this value is < 0, returns -1 *
  • if this value is = 0, returns 0 *
  • if this value is NaN, returns 0 *
* * @return an integer indicating the sign of this value */ public int signum() { if (hi > 0) return 1; if (hi < 0) return -1; if (lo > 0) return 1; if (lo < 0) return -1; return 0; } /** * Rounds this value to the nearest integer. * The value is rounded to an integer by adding 1/2 and taking the floor of the result. * Special cases: *
    *
  • If this value is NaN, returns NaN. *
* * @return this value rounded to the nearest integer */ public DD rint() { if (isNaN()) return this; // may not be 100% correct DD plus5 = this.add(0.5); return plus5.floor(); } /** * Returns the integer which is largest in absolute value and not further * from zero than this value. * Special cases: *
    *
  • If this value is NaN, returns NaN. *
* * @return the integer which is largest in absolute value and not further from zero than this value */ public DD trunc() { if (isNaN()) return NaN; if (isPositive()) return floor(); else return ceil(); } /** * Returns the absolute value of this value. * Special cases: *
    *
  • If this value is NaN, it is returned. *
* * @return the absolute value of this value */ public DD abs() { if (isNaN()) return NaN; if (isNegative()) return negate(); return new DD(this); } /** * Computes the square of this value. * * @return the square of this value. */ public DD sqr() { return this.multiply(this); } /** * Squares this object. * To prevent altering constants, * this method must only be used on values known to * be newly created. * * @return the square of this value. */ public DD selfSqr() { return this.selfMultiply(this); } /** * Computes the square of this value. * * @return the square of this value. */ public static DD sqr(double x) { return valueOf(x).selfMultiply(x); } /** * Computes the positive square root of this value. * If the number is NaN or negative, NaN is returned. * * @return the positive square root of this number. * If the argument is NaN or less than zero, the result is NaN. */ public DD sqrt() { /* Strategy: Use Karp's trick: if x is an approximation to sqrt(a), then sqrt(a) = a*x + [a - (a*x)^2] * x / 2 (approx) The approximation is accurate to twice the accuracy of x. Also, the multiplication (a*x) and [-]*x can be done with only half the precision. */ if (isZero()) return valueOf(0.0); if (isNegative()) { return NaN; } double x = 1.0 / Math.sqrt(hi); double ax = hi * x; DD axdd = valueOf(ax); DD diffSq = this.subtract(axdd.sqr()); double d2 = diffSq.hi * (x * 0.5); return axdd.add(d2); } public static DD sqrt(double x) { return valueOf(x).sqrt(); } /** * Computes the value of this number raised to an integral power. * Follows semantics of Java Math.pow as closely as possible. * * @param exp the integer exponent * @return x raised to the integral power exp */ public DD pow(int exp) { if (exp == 0.0) return valueOf(1.0); DD r = new DD(this); DD s = valueOf(1.0); int n = Math.abs(exp); if (n > 1) { /* Use binary exponentiation */ while (n > 0) { if (n % 2 == 1) { s.selfMultiply(r); } n /= 2; if (n > 0) r = r.sqr(); } } else { s = r; } /* Compute the reciprocal if n is negative. */ if (exp < 0) return s.reciprocal(); return s; } /*------------------------------------------------------------ * Ordering Functions *------------------------------------------------------------ */ /** * Computes the minimum of this and another DD number. * * @param x a DD number * @return the minimum of the two numbers */ public DD min(DD x) { if (this.le(x)) { return this; } else { return x; } } /** * Computes the maximum of this and another DD number. * * @param x a DD number * @return the maximum of the two numbers */ public DD max(DD x) { if (this.ge(x)) { return this; } else { return x; } } /*------------------------------------------------------------ * Conversion Functions *------------------------------------------------------------ */ /** * Converts this value to the nearest double-precision number. * * @return the nearest double-precision number to this value */ public double doubleValue() { return hi + lo; } /** * Converts this value to the nearest integer. * * @return the nearest integer to this value */ public int intValue() { return (int) hi; } /*------------------------------------------------------------ * Predicates *------------------------------------------------------------ */ /** * Tests whether this value is equal to 0. * * @return true if this value is equal to 0 */ public boolean isZero() { return hi == 0.0 && lo == 0.0; } /** * Tests whether this value is less than 0. * * @return true if this value is less than 0 */ public boolean isNegative() { return hi < 0.0 || (hi == 0.0 && lo < 0.0); } /** * Tests whether this value is greater than 0. * * @return true if this value is greater than 0 */ public boolean isPositive() { return hi > 0.0 || (hi == 0.0 && lo > 0.0); } /** * Tests whether this value is NaN. * * @return true if this value is NaN */ public boolean isNaN() { return Double.isNaN(hi); } /** * Tests whether this value is equal to another DoubleDouble value. * * @param y a DoubleDouble value * @return true if this value = y */ public boolean equals(DD y) { return hi == y.hi && lo == y.lo; } /** * Tests whether this value is greater than another DoubleDouble value. * @param y a DoubleDouble value * @return true if this value > y */ public boolean gt(DD y) { return (hi > y.hi) || (hi == y.hi && lo > y.lo); } /** * Tests whether this value is greater than or equals to another DoubleDouble value. * @param y a DoubleDouble value * @return true if this value >= y */ public boolean ge(DD y) { return (hi > y.hi) || (hi == y.hi && lo >= y.lo); } /** * Tests whether this value is less than another DoubleDouble value. * @param y a DoubleDouble value * @return true if this value < y */ public boolean lt(DD y) { return (hi < y.hi) || (hi == y.hi && lo < y.lo); } /** * Tests whether this value is less than or equal to another DoubleDouble value. * @param y a DoubleDouble value * @return true if this value <= y */ public boolean le(DD y) { return (hi < y.hi) || (hi == y.hi && lo <= y.lo); } /** * Compares two DoubleDouble objects numerically. * * @return -1,0 or 1 depending on whether this value is less than, equal to * or greater than the value of o */ public int compareTo(Object o) { DD other = (DD) o; if (hi < other.hi) return -1; if (hi > other.hi) return 1; if (lo < other.lo) return -1; if (lo > other.lo) return 1; return 0; } /*------------------------------------------------------------ * Output *------------------------------------------------------------ */ private static final int MAX_PRINT_DIGITS = 32; private static final DD TEN = DD.valueOf(10.0); private static final DD ONE = DD.valueOf(1.0); private static final String SCI_NOT_EXPONENT_CHAR = "E"; private static final String SCI_NOT_ZERO = "0.0E0"; /** * Dumps the components of this number to a string. * * @return a string showing the components of the number */ public String dump() { return "DD<" + hi + ", " + lo + ">"; } /** * Returns a string representation of this number, in either standard or scientific notation. * If the magnitude of the number is in the range [ 10-3, 108 ] * standard notation will be used. Otherwise, scientific notation will be used. * * @return a string representation of this number */ public String toString() { int mag = magnitude(hi); if (mag >= -3 && mag <= 20) return toStandardNotation(); return toSciNotation(); } /** * Returns the string representation of this value in standard notation. * * @return the string representation in standard notation */ public String toStandardNotation() { String specialStr = getSpecialNumberString(); if (specialStr != null) return specialStr; int[] magnitude = new int[1]; String sigDigits = extractSignificantDigits(true, magnitude); int decimalPointPos = magnitude[0] + 1; String num = sigDigits; // add a leading 0 if the decimal point is the first char if (sigDigits.charAt(0) == '.') { num = "0" + sigDigits; } else if (decimalPointPos < 0) { num = "0." + stringOfChar('0', -decimalPointPos) + sigDigits; } else if (sigDigits.indexOf('.') == -1) { // no point inserted - sig digits must be smaller than magnitude of number // add zeroes to end to make number the correct size int numZeroes = decimalPointPos - sigDigits.length(); String zeroes = stringOfChar('0', numZeroes); num = sigDigits + zeroes + ".0"; } if (this.isNegative()) return "-" + num; return num; } /** * Returns the string representation of this value in scientific notation. * * @return the string representation in scientific notation */ public String toSciNotation() { // special case zero, to allow as if (isZero()) return SCI_NOT_ZERO; String specialStr = getSpecialNumberString(); if (specialStr != null) return specialStr; int[] magnitude = new int[1]; String digits = extractSignificantDigits(false, magnitude); String expStr = SCI_NOT_EXPONENT_CHAR + magnitude[0]; // should never have leading zeroes // MD - is this correct? Or should we simply strip them if they are present? if (digits.charAt(0) == '0') { throw new IllegalStateException("Found leading zero: " + digits); } // add decimal point String trailingDigits = ""; if (digits.length() > 1) trailingDigits = digits.substring(1); String digitsWithDecimal = digits.charAt(0) + "." + trailingDigits; if (this.isNegative()) return "-" + digitsWithDecimal + expStr; return digitsWithDecimal + expStr; } /** * 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 y the number to extract ( >= 0) * @param decimalPointPos the position in which to insert a decimal point * @return the string containing the significant digits and possibly a decimal point */ private String extractSignificantDigits(boolean insertDecimalPoint, int[] magnitude) { DD y = this.abs(); // compute *correct* magnitude of y int mag = magnitude(y.hi); DD 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; } int decimalPointPos = mag + 1; StringBuffer buf = new StringBuffer(); int numDigits = MAX_PRINT_DIGITS - 1; for (int i = 0; i <= numDigits; i++) { if (insertDecimalPoint && i == decimalPointPos) { buf.append('.'); } 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(DD.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. */ int remMag = magnitude(y.hi); if (remMag < 0 && Math.abs(remMag) >= (numDigits - i)) continueExtractingDigits = false; if (! continueExtractingDigits) break; } magnitude[0] = mag; return buf.toString(); } /** * Creates a string of a given length containing the given character * * @param ch the character to be repeated * @param len the len of the desired string * @return the string */ private static String stringOfChar(char ch, int len) { StringBuffer buf = new StringBuffer(); for (int i = 0; i < len; i++) { buf.append(ch); } return buf.toString(); } /** * Returns the string for this value if it has a known representation. * (E.g. NaN or 0.0) * * @return the string for this special number * or null if the number is not a special number */ private String getSpecialNumberString() { if (isZero()) return "0.0"; if (isNaN()) return "NaN "; return null; } /** * Determines the decimal magnitude of a number. * The magnitude is the exponent of the greatest power of 10 which is less than * or equal to the number. * * @param x the number to find the magnitude of * @return the decimal magnitude of x */ private static int magnitude(double x) { double xAbs = Math.abs(x); double xLog10 = Math.log(xAbs) / Math.log(10); int xMag = (int) Math.floor(xLog10); /** * Since log computation is inexact, there may be an off-by-one error * in the computed magnitude. * Following tests that magnitude is correct, and adjusts it if not */ double xApprox = Math.pow(10, xMag); if (xApprox * 10 <= xAbs) xMag += 1; return xMag; } /*------------------------------------------------------------ * Input *------------------------------------------------------------ */ /** * 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: *
   * [+|-] {digit} [ . {digit} ] [ ( e | E ) [+|-] {digit}+
   * 
   * 
   * @param str the string to parse
   * @return the value of the parsed number
   * @throws NumberFormatException if str is not a valid representation of a number
   */
  public static DD parse(String str)
    throws NumberFormatException
  {
    int i = 0;
    int strlen = str.length();
    
    // skip leading whitespace
    while (Character.isWhitespace(str.charAt(i)))
      i++;
    
    // check for sign
    boolean isNegative = false;
    if (i < strlen) {
      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
    DD val = new DD();

    int numDigits = 0;
    int numBeforeDec = 0;
    int exp = 0;
    while (true) {
      if (i >= strlen)
        break;
      char ch = str.charAt(i);
      i++;
      if (Character.isDigit(ch)) {
        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') {
        String expStr = str.substring(i);
        // this should catch any format problems with the exponent
        try {
          exp = Integer.parseInt(expStr);
        }
        catch (NumberFormatException ex) {
          throw new NumberFormatException("Invalid exponent " + expStr + " in string " + str);  
        }
        break;
      }
      throw new NumberFormatException("Unexpected character '" + ch 
          + "' at position " + i 
          + " in string " + str);
    }
    DD val2 = val;
    
    // scale the number correctly
    int numDecPlaces = numDigits - numBeforeDec - exp;
    if (numDecPlaces == 0) {
      val2 = val;
    }
    else if (numDecPlaces > 0) {  
      DD scale = TEN.pow(numDecPlaces);
      val2 = val.divide(scale);
    }
    else if (numDecPlaces < 0) {
      DD scale = TEN.pow(-numDecPlaces);    
      val2 = val.multiply(scale);
    }
    // apply leading sign, if any
    if (isNegative) {
      return val2.negate();
    }
    return val2;

  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy