/// <summary> /// Computes the natural logarithm of a double double value. /// </summary> /// <param name="x">The argument of the logarithm.</param> /// <returns>The value of ln(x).</returns> public static DoubleDouble Log(DoubleDouble x) { double logHi = Math.Log(x.hi); if (ExtendedMath.IsNotFinite(logHi)) { return((DoubleDouble)logHi); } int e = (int)Math.Round(logHi / Global.LogTwo); if (e < 0) { x *= DoubleDouble.Pow(2.0, -e); } else if (e > 0) { x /= DoubleDouble.Pow(2.0, e); } // At this point 1/\sqrt{2} <= r <= \sqrt{2}, // i.e. 0.707 <= r <= 1.414 x -= DoubleDouble.One; // Now -0.293 <= r - 1 <= 0.414. // We have lost some accuracy if r ~ 1, i.e. x was very close // to an exact power of 2. return(e * log2 + Log1P(x)); }
/// <summary> /// Computes the square root of a double double value. /// </summary> /// <param name="x">The value of which the square root will be computed.</param> /// <returns>The value of the square root of x.</returns> public static DoubleDouble Sqrt(DoubleDouble x) { if (Double.IsNaN(x.hi)) { return(x); } if (x.hi < 0.0) { return(Double.NaN); } if (x.hi == 0.0) { return(0.0); } double yHi = Math.Sqrt(x.hi); double uHi, uLo; ExtendedMath.TwoProduct(yHi, yHi, out uHi, out uLo); double yLo = (((x.hi - uHi) - uLo) + x.lo) / (2.0 * yHi); return(new DoubleDouble(yHi, yLo)); }
/// <summary> /// Computes the product of two double double numbers. /// </summary> /// <param name="x">The first number.</param> /// <param name="y">The second number.</param> /// <returns>The product of <paramref name="x"/> and <paramref name="y"/>.</returns> public static DoubleDouble operator *(DoubleDouble x, DoubleDouble y) { ExtendedMath.TwoProduct(x.hi, y.hi, out double p0, out double p1); if (p0 == 0.0 || ExtendedMath.IsNotFinite(p0)) { return((DoubleDouble)p0); } ExtendedMath.TwoProduct(x.hi, y.lo, out double p2, out double p4); ExtendedMath.TwoProduct(x.lo, y.hi, out double p3, out double p5); double p6 = x.lo * y.lo; ExtendedMath.ThreeSum(p1, p2, p3, out double t1, out double t2); t2 += p4 + p5 + p6; ExtendedMath.ThreeSum(p0, t1, t2, out double pHi, out double pLo); return(new DoubleDouble(pHi, pLo)); /* * double zHi, zLo; * ExtendedMath.TwoProduct(a.hi, b.hi, out zHi, out zLo); * zLo = (zLo + a.hi * b.lo) + a.lo * b.hi; * //zLo = (a.hi * b.lo + a.lo * b.hi) + zLo; * * return new DoubleDouble(zHi, zLo); */ }
/// <summary> /// Computes the quotient of two double double numbers. /// </summary> /// <param name="a">The dividend.</param> /// <param name="b">The divisor.</param> /// <returns>The value of <paramref name="a"/> / <paramref name="b"/>.</returns> public static DoubleDouble operator /(DoubleDouble a, DoubleDouble b) { double q1 = a.hi / b.hi; DoubleDouble r = a - q1 * b; double q2 = r.hi / b.hi; r = r - q2 * b; double q3 = r.hi / b.hi; double qHi, qLo; double t1, t2, t3; ExtendedMath.TwoSum(q1, q2, out t1, out t2); ExtendedMath.TwoSum(q3, t1, out qHi, out t3); ExtendedMath.TwoSum(t2, t3, out qLo, out t1); return(new DoubleDouble(qHi, qLo)); /* * // Compute an initial approximation by dividing hi parts. * double qHi = a.hi / b.hi; * * // Compute the product of qHi and bHi to full precision. * // Ideally this would be exactly aHi but it won't be. * double uHi, uLo; * ExtendedMath.TwoProduct(qHi, b.hi, out uHi, out uLo); * * // Now compute the correction and but it in qLo. * double qLo = ((((a.hi - uHi) - uLo) + a.lo) - qHi * b.lo) / b.hi; * * return new DoubleDouble(qHi, qLo); */ }
public static void ThreeSum(double a, double b, double c, out double sum, out double err) { double t1, t2, t3; ExtendedMath.TwoSum(a, b, out t1, out t2); ExtendedMath.TwoSum(c, t1, out sum, out t3); ExtendedMath.TwoSum(t2, t3, out err, out t1); }
/// <summary> /// Computes the sum of two double double numbers. /// </summary> /// <param name="x">The first number.</param> /// <param name="y">The second number.</param> /// <returns>The value of <paramref name="x"/> + <paramref name="y"/>.</returns> public static DoubleDouble operator +(DoubleDouble x, DoubleDouble y) { // Add high components ExtendedMath.TwoSum(x.hi, y.hi, out double sHi, out double sLo); if (ExtendedMath.IsNotFinite(sHi)) { return((DoubleDouble)sHi); } // Add low components ExtendedMath.TwoSum(x.lo, y.lo, out double tHi, out double tLo); ExtendedMath.TwoSum(sHi, sLo + tHi, out double vHi, out double vLo); ExtendedMath.FastTwoSum(vHi, tLo + vLo, out double zHi, out double zLo); return(new DoubleDouble(zHi, zLo)); }
/// <summary> /// Computes the quotient of two double double numbers. /// </summary> /// <param name="x">The dividend.</param> /// <param name="y">The divisor.</param> /// <returns>The value of <paramref name="x"/> / <paramref name="y"/>.</returns> public static DoubleDouble operator /(DoubleDouble x, DoubleDouble y) { double q1 = x.hi / y.hi; // If leading order result is NaN or infinity or zero, we are done. // To continue would introduce NaNs even if result is infinite, so this early return is necessary. if (q1 == 0.0 || ExtendedMath.IsNotFinite(q1)) { return((DoubleDouble)q1); } DoubleDouble r = x - q1 * y; double q2 = r.hi / y.hi; r = r - q2 * y; double q3 = r.hi / y.hi; double qHi, qLo; double t1, t2, t3; ExtendedMath.TwoSum(q1, q2, out t1, out t2); ExtendedMath.TwoSum(q3, t1, out qHi, out t3); ExtendedMath.TwoSum(t2, t3, out qLo, out t1); return(new DoubleDouble(qHi, qLo)); /* * // Compute an initial approximation by dividing hi parts. * double qHi = a.hi / b.hi; * * // Compute the product of qHi and bHi to full precision. * // Ideally this would be exactly aHi but it won't be. * double uHi, uLo; * ExtendedMath.TwoProduct(qHi, b.hi, out uHi, out uLo); * * // Now compute the correction and but it in qLo. * double qLo = ((((a.hi - uHi) - uLo) + a.lo) - qHi * b.lo) / b.hi; * * return new DoubleDouble(qHi, qLo); */ }
/// <summary> /// Computes the square root of a double double value. /// </summary> /// <param name="x">The value of which the square root will be computed.</param> /// <returns>The value of the square root of x.</returns> public static DoubleDouble Sqrt(DoubleDouble x) { if (x.hi == 0.0) { return(0.0); } double yHi = Math.Sqrt(x.hi); if (ExtendedMath.IsNotFinite(yHi)) { return((DoubleDouble)yHi); } ExtendedMath.TwoProduct(yHi, yHi, out double uHi, out double uLo); double yLo = (((x.hi - uHi) - uLo) + x.lo) / (2.0 * yHi); return(new DoubleDouble(yHi, yLo)); }
/// <summary> /// Computes the sum of two double double numbers. /// </summary> /// <param name="a">The first number.</param> /// <param name="b">The second number.</param> /// <returns>The value of <paramref name="a"/> + <paramref name="b"/>.</returns> public static DoubleDouble operator +(DoubleDouble a, DoubleDouble b) { // Add high components double sHi, sLo; ExtendedMath.TwoSum(a.hi, b.hi, out sHi, out sLo); // Add low components double tHi, tLo; ExtendedMath.TwoSum(a.lo, b.lo, out tHi, out tLo); double vHi, vLo; ExtendedMath.TwoSum(sHi, sLo + tHi, out vHi, out vLo); double zHi, zLo; ExtendedMath.FastTwoSum(vHi, tLo + vLo, out zHi, out zLo); return(new DoubleDouble(zHi, zLo)); }
/// <summary> /// Computes the product of two double double numbers. /// </summary> /// <param name="a">The first number.</param> /// <param name="b">The second number.</param> /// <returns>The product of <paramref name="a"/> and <paramref name="b"/>.</returns> public static DoubleDouble operator *(DoubleDouble a, DoubleDouble b) { double p0, p1; ExtendedMath.TwoProduct(a.hi, b.hi, out p0, out p1); double p2, p4; ExtendedMath.TwoProduct(a.hi, b.lo, out p2, out p4); double p3, p5; ExtendedMath.TwoProduct(a.lo, b.hi, out p3, out p5); double p6 = a.lo * b.lo; double t1, t2; ExtendedMath.ThreeSum(p1, p2, p3, out t1, out t2); t2 += p4 + p5 + p6; double pHi, pLo; ExtendedMath.ThreeSum(p0, t1, t2, out pHi, out pLo); return(new DoubleDouble(pHi, pLo)); /* * double zHi, zLo; * ExtendedMath.TwoProduct(a.hi, b.hi, out zHi, out zLo); * zLo = (zLo + a.hi * b.lo) + a.lo * b.hi; * //zLo = (a.hi * b.lo + a.lo * b.hi) + zLo; * * return new DoubleDouble(zHi, zLo); */ }
/// <summary> /// Produces a text representation of the double double value using the given format provider. /// </summary> /// <param name="format">The format provider.</param> /// <returns>A text representation of the value.</returns> private string ToString(IFormatProvider format) { // This algorithm doesn't handle infinities, NaNs, and zeros, but writing them is trivial. if (ExtendedMath.IsNotFinite(hi) || hi == 0.0) { return(hi.ToString(format)); } // Multiply by a power of 10 to put leading digit in the ones place Debug.Assert(hi != 0.0); int e = (int)Math.Floor(Math.Log10(Math.Abs(hi))); DoubleDouble r = Abs(this); if (e > 0) { r /= Pow(ten, e); } else if (e < 0) { r *= Pow(ten, -e); } // If scaling has screwed us up because of overflow, exit. Fix this. if (Double.IsNaN(r.hi) || Double.IsNaN(r.lo)) { return("X"); } // Spit out up to 32 digits StringBuilder s = new StringBuilder(); int z = 0; for (int i = 0; i < 32; i++) { if (r.hi == 0.0) { break; } double t = Math.Floor(r.hi); /* If nothing left, we can stop early */ if (t < 0) { t = 0.0; /* if we subtract to zero, sometimes the result can be a very tiny negative number that floors to -1 */ } int d = (int)t; Debug.Assert((0 <= d) && (d < 10)); s.Append(d); if (d == 0) { z++; } else { z = 0; } /* Keep track of number of trailing zeros for later truncation */ r -= t; r *= ten; } // Remove trailing zeros (which are all after the decimal point) if (z > 0) { s.Remove(s.Length - z, z); } // Express in scientific notation for large exponents, // or by filling in zeros for small exponents. if (e >= 6) { if (s.Length > 1) { s.Insert(1, '.'); } s.Append($"E{e}"); } else if (e >= 0) { if (s.Length > e + 1) { s.Insert(e + 1, '.'); } else { s.Append("000000", 0, e - s.Length + 1); } } else if (e >= -4) { s.Insert(0, "0.00000".Substring(0, 1 - e)); /* StringBuilder.Append has a substring overload, but StringBuilder.Insert doesn't */ } else { if (s.Length > 1) { s.Insert(1, '.'); } s.Append($"E{e}"); } // Add a negative sign if necessary if (hi < 0.0) { s.Insert(0, '-'); } return(s.ToString()); }