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

net.sf.jagg.math.DoubleDouble Maven / Gradle / Ivy

Go to download

jAgg is a Java 5.0 API that supports “group by” operations on Lists of Java objects: aggregate operations such as count, sum, max, min, avg, and many more. It also allows custom aggregate operations.

The newest version!
package net.sf.jagg.math;

/**
 * A DoubleDouble is used when extra precision is necessary to
 * cut way down on floating point errors.
 *
 * @author Randy Gettman
 * @since 0.4.0
 */
public strictfp class DoubleDouble implements Comparable
{
   /**
    * The DoubleDouble NaN (Not a Number), immutable.
    */
   public static final DoubleDouble NaN = new ImmutableDoubleDouble(Double.NaN, 0);
   /**
    * The DoubleDouble zero, immutable.
    */
   public static final DoubleDouble ZERO = new ImmutableDoubleDouble();
   
   /**
    * Used to "split" a double into two parts, each with 26 bits.
    */
   private static final double SPLIT = 134217729.0;  // 2 to the 27th + 1

   private double myHigh;
   private double myLow;

   /**
    * Create a DoubleDouble, initialized to zero.
    */
   public DoubleDouble()
   {
      myHigh = 0;
      myLow = 0;
   }

   /**
    * Create a DoubleDouble from a double.
    * @param d A double.
    */
   public DoubleDouble(double d)
   {
      myHigh = d;
      myLow = 0;
   }

   /**
    * Create a DoubleDouble from high and low parts.
    * @param hi The high-order part.
    * @param lo The low-order part.
    */
   public DoubleDouble(double hi, double lo)
   {
      normalize(hi, lo, 0);
   }

   /**
    * Copy constructor.
    * @param dd Another DoubleDouble.
    */
   public DoubleDouble(DoubleDouble dd)
   {
      myHigh = dd.myHigh;
      myLow = dd.myLow;
   }

   /**
    * Sets this DoubleDouble equal to zero.
    */
   public void reset()
   {
      myHigh = 0;
      myLow = 0;
   }

   /**
    * Returns the double that is closest in value to this
    * DoubleDouble.
    * @return A double (the high portion of this
    *    DoubleDouble).
    */
   public double doubleValue()
   {
      return myHigh;
   }

   /**
    * Returns the low-order portion of this DoubleDouble.
    * @return The low-order portion of this DoubleDouble.
    */
   public double getLow()
   {
      return myLow;
   }

   /**
    * Returns whether this DoubleDouble is NaN.
    * @return Whether this DoubleDouble is NaN.
    */
   public boolean isNaN()
   {
      return Double.isNaN(myHigh);
   }

   /**
    * Adds another DoubleDouble to this one.
    * @param dd Another DoubleDouble.
    */
   public void addToSelf(DoubleDouble dd)
   {
      if (isNaN())
         return;
      // Algorithm is based on "Algorithms for Quad-Double Precision Floating
      // Point Arithmetic" by Hida, Li, and Bailey, 2000, Berkeley.
      double e, e2, e3, f, s0, s1, v;
      // Two Sum: "a" is myHigh, "b" is dd.myHigh, "s" is s0, "e" is e.
      s0 = myHigh + dd.myHigh;
      v = s0 - myHigh;
      e = (myHigh - (s0 - v)) + (dd.myHigh - v);
      // Two Sum: "a" is myLow, "b" is dd.myLow, "s" is f, "e" is e2.
      f = myLow + dd.myLow;
      v = f - myLow;
      e2 = (myLow - (f - v)) + (dd.myLow - v);
      // Two Sum: "a" is f, "b" is e, "s" is s1, "e" is e3.
      s1 = f + e;
      v = s1 - f;
      e3 = (f - (s1 - v)) + (e - v);
      // Straight sum: e is reused.
      e = e2 + e3;

      normalize(s0, s1, e);
   }

   /**
    * Adds a double to this.  Algorithm is based on "Algorithms
    * for Quad-Double Precision Floating Point Arithmetic" by Hida, Li, and
    * Bailey, 2000, Berkeley.
    * @param d A double.
    */
   public void addToSelf(double d)
   {
      if (isNaN())
         return;
      // Algorithm is based on "Algorithms for Quad-Double Precision Floating
      // Point Arithmetic" by Hida, Li, and Bailey, 2000, Berkeley.
      double e, s0, s1, v;
      // Two Sum: "a" is myHigh, "b" is d, "s" is s0, "e" is e.
      s0 = myHigh + d;
      v = s0 - myHigh;
      e = (myHigh - (s0 - v)) + (d - v);
      // Two Sum: "a" is myLow, "b" is e, "s" is s1, "e" is e.
      s1 = myLow + e;
      v = s1 - myLow;
      e = (myLow - (s1 - v)) + (e - v);

      normalize(s0, s1, e);
   }

   /**
    * Subtracts another DoubleDouble from this one.
    * @param dd Another DoubleDouble.
    */
   public void subtractFromSelf(DoubleDouble dd)
   {
      DoubleDouble negative = new DoubleDouble(dd);
      negative.negateSelf();
      addToSelf(negative);
   }

   /**
    * Subtracts a double from this.
    * @param d A double.
    */
   public void subtractFromSelf(double d)
   {
      addToSelf(-d);
   }

   /**
    * Negate each part.  NaNs aren't negated (or negatable).
    * The negation of NaN is NaN.
    */
   public void negateSelf()
   {
      myHigh = -myHigh;
      myLow = -myLow;
   }

   /**
    * Multiplies self by another DoubleDouble.
    * @param dd Another DoubleDouble.
    */
   public void multiplySelfBy(DoubleDouble dd)
   {
      if (isNaN())
         return;
      double ah, al, bh, bl, ch, cl, e0, e1, e2, p0, p1, p2, v;
      // Algorithm is based on "Algorithms for Quad-Double Precision Floating
      // Point Arithmetic" by Hida, Li, and Bailey, 2000, Berkeley.
      // Two Prod: "a" is myHigh, "b" is dd.myHigh, "s" is p0, "e" is e0.
      p0 = myHigh * dd.myHigh;
      // Split: "a" is myHigh.
      v = SPLIT * myHigh;
      ah = v - (v - myHigh);
      al = myHigh - ah;
      // Split: "a" is dd.myHigh.
      v = SPLIT * dd.myHigh;
      bh = v - (v - dd.myHigh);
      bl = dd.myHigh - bh;
      e0 = (((ah * bh - p0) + ah * bl) + al * bh) + al * bl;

      // Two Prod: "a" is myHigh, "b" is dd.myLow, "s" is p1, "e" is e1.
      p1 = myHigh * dd.myLow;
      // Store the splits from dd.myHigh in (ch, cl) to avoid recomputing them
      // later.
      ch = bh;
      cl = bl;
      // Split with myHigh is still in (ah, al).
      // Split: "a" is dd.myLow.
      v = SPLIT * dd.myLow;
      bh = v - (v - dd.myLow);
      bl = dd.myLow - bh;
      e1 = (((ah * bh - p1) + ah * bl) + al * bh) + al * bl;

      // Two Prod: "a" is myLow, "b" is dd.myHigh, "s" is p2, "e" is e2.
      p2 = myLow * dd.myHigh;
      // Split: "a" is myLow.
      v = SPLIT * myLow;
      ah = v - (v - myLow);
      al = myLow - ah;
      // Split with dd.myHigh still in (ch, cl).
      e2 = (((ah * ch - p2) + ah * cl) + al * ch) + al * cl;
      // Note: ah, al, bh, bl, ch, cl can now be reused.

      // low-order terms
      // Three Sum: "x" is e0, "y" is p1, "z" is p2, "r0" is p1 (reused), "r1" is e0 (reused).
      // Two Sum: "a" is e0, "b" is p1, "s" is bh, "e" is bl.
      bh = e0 + p1;
      v = (bh - e0);
      bl = (e0 - (bh - v)) + (p1 - v);
      // Note: e0, p0, and p1 can now be reused.
      // Two Sum: "a" is bh, "b" is p2, "s" is p1, "e" is e0.
      p1 = bh + p2;
      v = (p1 - bh);
      e0 = (bh - (p1 - v)) + (p2 - v);
      // Use normal addition here.  Gather the othe error in the three sum
      // (bl), then add the lower-order terms.  This includes the
      // multiplication of the low-order terms, which can also be done in
      // normal multiplication.
      e0 += bl + (myLow * dd.myLow) + e1 + e2;

      normalize(p0, p1, e0);
   }

   /**
    * Multiplies self by a double.
    * @param d A double.
    */
   public void multiplySelfBy(double d)
   {
      if (isNaN())
         return;
      // Algorithm is based on "Algorithms for Quad-Double Precision Floating
      // Point Arithmetic" by Hida, Li, and Bailey, 2000, Berkeley.
      double ah, al, bh, bl, e, e2, f, p0, p1, v;
      // Two Prod: "a" is myHigh, "b" is d, "p" is p0, "e" is e.
      p0 = myHigh * d;
      // Split: "a" is myHigh.
      v = SPLIT * myHigh;
      ah = v - (v - myHigh);
      al = myHigh - ah;
      // Split: "a" is d.
      v = SPLIT * d;
      bh = v - (v - d);
      bl = d - bh;
      e = (((ah * bh - p0) + ah * bl) + al * bh) + al * bl;
      // Two Prod: "a" is myLow, "b" is d, "p" is f, "e" is e2.
      f = myLow * d;
      // Split: "a" is myLow.
      v = SPLIT * myLow;
      ah = v - (v - myLow);
      al = myLow - ah;
      // Split: The splits from d are still in (bh, bl).
      e2 = (((ah * bh - f) + ah * bl) + al * bh) + al * bl;
      // Two Sum: "a" is e, "b" is f, "s" is p1, "e" is e (reused).
      p1 = e + f;
      v = p1 - e;
      e = (e - (p1 - v)) + (f - v);
      // Normal add
      e += e2;

      normalize(p0, p1, e);
   }

   /**
    * Squares self.
    */
   public void squareSelf()
   {
      if (isNaN())
         return;
      double ah, al, bh, bl, e0, e1, p0, p1, v;
      // Algorithm is based on "Algorithms for Quad-Double Precision Floating
      // Point Arithmetic" by Hida, Li, and Bailey, 2000, Berkeley.
      // Two Prod: "a" is myHigh, "b" is myHigh, "s" is p0, "e" is e0.
      p0 = myHigh * myHigh;
      // Split: "a" is myHigh.
      v = SPLIT * myHigh;
      ah = v - (v - myHigh);
      al = myHigh - ah;
      e0 = (((ah * ah - p0) + ah * al) + al * ah) + al * al;

      // Two Prod: "a" is myHigh, "b" is myLow, "s" is p1, "e" is e1.
      p1 = myHigh * myLow;
      // Split with myHigh is still in (ah, al).
      // Split: "a" is myLow.
      v = SPLIT * myLow;
      bh = v - (v - myLow);
      bl = myLow - bh;
      e1 = (((ah * bh - p1) + ah * bl) + al * bh) + al * bl;

      // The high-low term is doubled.
      p1 *= 2;
      e1 *= 2;

      // low-order terms
      // Two Sum: "a" is e0, "b" is p1, "s" is bh (reused), "e" is bl (reused).
      bh = e0 + p1;
      v = (bh - e0);
      bl = (e0 - (bh - v)) + (p1 - v);
      // Use normal addition here.  This includes the multiplication of the
      // low-order terms, which can also be done in normal multiplication.
      bl += (myLow * myLow) + e1;

      normalize(p0, bh, bl);
   }

   /**
    * Divides self by a DoubleDouble.
    * @param dd Another DoubleDouble.
    */
   public void divideSelfBy(DoubleDouble dd)
   {
      // Karp's method for High-Precision Division.
      double x, y;
      DoubleDouble r;
      x = 1.0 / dd.myHigh;
      y = myHigh * x;
      r = new DoubleDouble(dd);
      r.multiplySelfBy(y);
      r.negateSelf();
      r.addToSelf(this);
      r.multiplySelfBy(x);
      r.addToSelf(y);

      myHigh = r.myHigh;
      myLow = r.myLow;
   }

   /**
    * Divides self by a double.
    * @param d A double.
    */
   public void divideSelfBy(double d)
   {
      // Karp's method for High-Precision Division.
      double x, y;
      DoubleDouble r;
      x = 1.0 / d;
      y = myHigh * x;
      r = new DoubleDouble(d);
      r.multiplySelfBy(y);
      r.negateSelf();
      r.addToSelf(this);
      r.multiplySelfBy(x);
      r.addToSelf(y);

      myHigh = r.myHigh;
      myLow = r.myLow;
   }

   /**
    * Takes the square root of self.
    */
   public void sqrtSelf()
   {
      // Karp's method for High-Precision Square Root.
      double x, y;
      DoubleDouble r;
      x = 1.0 / Math.sqrt(myHigh);
      y = myHigh * x;
      r = new DoubleDouble(y);
      r.squareSelf();
      r.negateSelf();
      r.addToSelf(this);
      r.multiplySelfBy(x);
      r.divideSelfBy(2.0);
      r.addToSelf(y);

      myHigh = r.myHigh;
      myLow = r.myLow;
   }

   /**
    * Raise self to an integer exponent.
    * @param exponent The exponent.
    */
   public void powSelf(long exponent)
   {
      if (isNaN())
         return;
      if (exponent == 0)
      {
         if (myHigh == 0)
         {
            // 0^0 = NaN
            myHigh = Double.NaN;
            myLow = 0;
         }
         else
         {
            // x^0 = 1
            myHigh = 1;
            myLow = 0;
         }
      }
      // 0^y = 0, 1^y = 1.
      if (myHigh == 0 || (myHigh == 1 && myLow == 0))
         return;
      
      boolean invert = exponent < 0;
      exponent = Math.abs(exponent);

      if (exponent == 2)
         squareSelf();
      else if (exponent > 2)
      {
         // Exponentiation by repeated squaring.
         DoubleDouble result = new DoubleDouble(1.0);
         DoubleDouble square = new DoubleDouble(this);
         while(exponent >= 1)
         {
            if ((exponent & 1) == 1) // if odd
               result.multiplySelfBy(square);
            square.squareSelf();
            exponent >>>= 1;  // int divide by 2
         }
         myHigh = result.myHigh;
         myLow = result.myLow;
      }

      if (invert)
      {
         DoubleDouble reciprocal = new DoubleDouble(1.0);
         reciprocal.divideSelfBy(this);
         myHigh = reciprocal.myHigh;
         myLow = reciprocal.myLow;
      }
   }

   /**
    * Takes the nth root of self.
    * @param n The root.
    */
   public void nthRootSelf(long n)
   {
      // Modified Karp's method.
      double x, y;
      DoubleDouble r;
      x = Math.pow(myHigh, (1.0 - n) / n);
      y = myHigh * x;
      r = new DoubleDouble(y);
      r.powSelf(n);
      r.negateSelf();
      r.addToSelf(this);
      r.multiplySelfBy(x);
      r.divideSelfBy(n);
      r.addToSelf(y);

      myHigh = r.myHigh;
      myLow = r.myLow;
   }

   /**
    * Normalize this Double following an arithmetic computation.
    * @param s0 The high order term.
    * @param s1 The low order term.
    * @param e The error.
    */
   private void normalize(double s0, double s1, double e)
   {
      double t0, t1, t2, v;
      int k = 0;
      // Normalize: "a0" is s0, "a1" is s1, "a2" is e.
      // Quick Two Sum: "a" is s1, "b" is e, "s" is v (reused variable), "e" is t2.
      v = s1 + e;
      t2 = e - (v - s1);
      // Quick Two Sum: "a" is s0, "b" is v, "s" is t0, "e" is t1.
      t0 = s0 + v;
      t1 = v - (t0 - s0);

      myHigh = myLow = 0;
      // Quick Two Sum: "a" is t0, "b" is t1, "s" is v (reused variable), "e" is e.
      v = t0 + t1;
      e = t1 - (v - t0);

      if (v != 0)
      {
         myHigh = v;
         myLow = 0;
         v = e;
         k++;
      }
      // Quick Two Sum: "a" is v, "b" is t2, "s" is s0 (reused variable), "e" is e.
      s0 = v + t2;
      // Calculation of e only if needed later (in myLow if k == 0).
      if (s0 != 0)
      {
         if (k == 0)
         {
            myHigh = s0;
            myLow = t2 - (s0 - v);
         }
         else
         {
            myLow = s0;
         }
      }
   }

   /**
    * Returns an integer less than zero, equal to zero, or greater than zero,
    * depending on whether this compares less than, equal to, or greather than
    * another DoubleDouble.
    * @param other Another DoubleDouble.
    * @return An integer less than zero, equal to zero, or greater than zero,
    *    depending on whether this compares less than, equal to, or greather
    *    than another DoubleDouble.
    */
   public int compareTo(DoubleDouble other)
   {
      if (myHigh < other.myHigh)
         return -1;
      if (myHigh > other.myHigh)
         return 1;
      if (myLow < other.myLow)
         return -1;
      if (myLow > other.myLow)
         return 1;
      return 0;
   }

   /**
    * Used to initialize constants.  All operations that change the contents
    * throw UnsupportedOperationException to prevent it.
    */
   private static strictfp class ImmutableDoubleDouble extends DoubleDouble
   {
      /**
       * Effectively creates an ImmutableDoubleDouble constant for
       * zero.
       */
      public ImmutableDoubleDouble()
      {
         super(0, 0);
      }

      /**
       * Construct an ImmutableDoubleDouble.
       * @param hi The high-order part.
       * @param lo The low-order part.
       */
      public ImmutableDoubleDouble(double hi, double lo)
      {
         super(hi, lo);
      }

      public void addToSelf(DoubleDouble dd) { notSupported(); }
      public void addToSelf(double d) { notSupported(); }
      public void divideSelfBy(DoubleDouble dd) { notSupported(); }
      public void divideSelfBy(double d) { notSupported(); }
      public void multiplySelfBy(DoubleDouble dd) { notSupported(); }
      public void multiplySelfBy(double d) { notSupported(); }
      public void negateSelf() { notSupported(); }
      public void nthRootSelf(long n) { notSupported(); }
      public void powSelf(long n) { notSupported(); }
      public void reset() { notSupported(); }
      public void sqrtSelf() { notSupported(); }
      public void squareSelf() { notSupported(); }
      public void subtractFromSelf(DoubleDouble dd) { notSupported(); }
      public void subtractFromSelf(double d) { notSupported(); }

      private void notSupported()
      {
         throw new UnsupportedOperationException("Can't modify constant DoubleDouble value.");
      }
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy