Пример #1
0
        public static double Cos(long z0, double z1)
        {
            switch (MoreMath.Mod(z0, 4L))
            {
            case 0L:
                return(Math.Cos(Math.PI / 2.0 * z1));

            case 1L:
                return(-Math.Sin(Math.PI / 2.0 * z1));

            case 2L:
                return(-Math.Cos(Math.PI / 2.0 * z1));

            case 3L:
                return(Math.Sin(Math.PI / 2.0 * z1));

            default:
                throw new InvalidOperationException();
            }
        }
Пример #2
0
        /// <summary>
        /// Computes the tangent of a complex number.
        /// </summary>
        /// <param name="z">The argument.</param>
        /// <returns>The value of tan(z).</returns>
        public static Complex Tan(Complex z)
        {
            // tan z = [sin(2x) + I sinh(2y)]/[cos(2x) + I cosh(2y)]
            double x2   = 2.0 * z.Re;
            double y2   = 2.0 * z.Im;
            double p    = Math.Exp(y2);
            double q    = 1 / p;
            double cosh = (p + q) / 2.0;

            if (Math.Abs(z.Im) < 4.0)
            {
                double sinh = (p - q) / 2.0;
                double D    = MoreMath.Cos(x2) + cosh;
                return(new Complex(MoreMath.Sin(x2) / D, sinh / D));
            }
            else
            {
                // when Im(z) gets too large, sinh and cosh individually blow up
                // but ratio is still ~1, so rearrage to use tanh instead
                double F = (1.0 + Math.Cos(x2) / cosh);
                return(new Complex(MoreMath.Sin(x2) / cosh / F, Math.Tanh(y2) / F));
            }
        }
Пример #3
0
        /// <summary>
        /// Computes e raised to the power of a complex number.
        /// </summary>
        /// <param name="z">The argument.</param>
        /// <returns>The value of e<sup>z</sup>.</returns>
        public static Complex Exp(Complex z)
        {
            double m = Math.Exp(z.Re);

            return(new Complex(m * MoreMath.Cos(z.Im), m * MoreMath.Sin(z.Im)));
        }
Пример #4
0
        private static void Asin_Internal(double x, double y, out double b, out double bPrime, out double v)
        {
            // This method for the inverse complex sine (and cosine) is described in
            // Hull and Tang, "Implementing the Complex Arcsine and Arccosine Functions Using Exception Handling",
            // ACM Transactions on Mathematical Software (1997)
            // (https://www.researchgate.net/profile/Ping_Tang3/publication/220493330_Implementing_the_Complex_Arcsine_and_Arccosine_Functions_Using_Exception_Handling/links/55b244b208ae9289a085245d.pdf)

            // First, the basics: start with sin(w) = \frac{e^{iw} - e^{-iw}}{2i} = z. Here z is the input
            // and w is the output. To solve for w, define t = e^{i w} and multiply through by t to
            // get the quadratic equation t^2 - 2 i z t - 1 = 0. The solution is t = i z + \sqrt{1 - z^2}, so
            //   w = arcsin(z) = - i \ln ( i z + \sqrt{1 - z^2} )
            // Decompose z = x + i y, multiply out i z + \sqrt{1 - z^2}, use \ln s = |s| + i arg(s), and do a
            // bunch of algebra to get the components of w = arcsin(z) = u + i v
            //   u = arcsin(\beta)  v = sign(y) \ln (\alpha + \sqrt{\alpha^2 - 1})
            // where
            //   \alpha = \frac{\rho + \sigma}{2}  \beta = \frac{\rho - \sigma}{2}
            //   \rho = \sqrt{(x + 1)^2 + y^2}  \sigma = \sqrt{(x - 1)^2 + y^2}
            // This appears in DLMF section 4.23. (http://dlmf.nist.gov/4.23), along with the analogous
            //   arccos(w) = arccos(\beta) - i sign(y) \ln (\alpha + \sqrt{\alpha^2 - 1})
            // So \alpha and \beta together give us arcsin(w) and arccos(w).

            // As written, \alpha is not susceptable to cancelation errors, but \beta is. To avoid cancelation, note
            //   \beta = \frac{\rho^2 - \sigma^2}{2(\rho + \sigma)} = \frac{2 x}{\rho + \sigma} = \frac{x}{\alpha}
            // which is not subject to cancelation. Note \alpha >= 1 and |\beta| <= 1.

            // For \alpha ~ 1, the argument of the log is near unity, so we compute (\alpha - 1) instead,
            // and write the argument as 1 + (\alpha - 1) + \sqrt{(\alpha - 1)(\alpha + 1)}.
            // For \beta ~ 1, arccos does not accurately resolve small angles, so we compute the tangent of the angle
            // instead. Hull and Tang derive formulas for (\alpha - 1) and \beta' = \tan(u) that do not suffer
            // cancelation for these cases.

            // For simplicity, we assume all positive inputs and return all positive outputs. The caller should
            // assign signs appropriate to the desired cut conventions. We return v directly since its magnitude
            // is the same for both arcsin and arccos. Instead of u, we usually return \beta and sometimes \beta'.
            // If \beta' is not computed, it is set to -1; if it is computed, it should be used instead of \beta
            // to determine u. Compute u = \arcsin(\beta) or u = \arctan(\beta') for arcsin, u = \arccos(\beta)
            // or \arctan(1/\beta') for arccos.

            Debug.Assert((x >= 0.0) || Double.IsNaN(x));
            Debug.Assert((y >= 0.0) || Double.IsNaN(y));

            // For x or y large enough to overflow \alpha^2, we can simplify our formulas and avoid overflow.
            if ((x > Global.SqrtMax / 2.0) || (y > Global.SqrtMax / 2.0))
            {
                b      = -1.0;
                bPrime = x / y;

                double small, big;
                if (x < y)
                {
                    small = x;
                    big   = y;
                }
                else
                {
                    small = y;
                    big   = x;
                }
                v = Global.LogTwo + Math.Log(big) + 0.5 * MoreMath.LogOnePlus(MoreMath.Sqr(small / big));
            }
            else
            {
                double r = MoreMath.Hypot((x + 1.0), y);
                double s = MoreMath.Hypot((x - 1.0), y);

                double a = (r + s) / 2.0;
                b = x / a;
                //Debug.Assert(a >= 1.0);
                //Debug.Assert(Math.Abs(b) <= 1.0);

                if (b > 0.75)
                {
                    //double amx;
                    if (x <= 1.0)
                    {
                        double amx = (y * y / (r + (x + 1.0)) + (s + (1.0 - x))) / 2.0;
                        bPrime = x / Math.Sqrt((a + x) * amx);
                    }
                    else
                    {
                        // In this case, amx ~ y^2. Since we take the square root of amx, we should
                        // pull y out from under the square root so we don't loose its contribution
                        // when y^2 underflows.
                        double t = (1.0 / (r + (x + 1.0)) + 1.0 / (s + (x - 1.0))) / 2.0;
                        bPrime = x / Math.Sqrt((a + x) * t) / y;
                    }
                    //Debug.Assert(amx >= 0.0);
                    //bPrime = x / Math.Sqrt((a + x) * amx);
                }
                else
                {
                    bPrime = -1.0;
                }

                if (a < 1.5)
                {
                    if (x < 1.0)
                    {
                        // This is another case where our expression is proportional to y^2 and
                        // we take its square root, so again we pull out a factor of y from
                        // under the square root. Without this trick, our result has zero imaginary
                        // part for y smaller than about 1.0E-155, while the imaginary part
                        // should be about y.
                        double t   = (1.0 / (r + (x + 1.0)) + 1.0 / (s + (1.0 - x))) / 2.0;
                        double am1 = y * y * t;
                        v = MoreMath.LogOnePlus(am1 + y * Math.Sqrt(t * (a + 1.0)));
                    }
                    else
                    {
                        double am1 = (y * y / (r + (x + 1.0)) + (s + (x - 1.0))) / 2.0;
                        v = MoreMath.LogOnePlus(am1 + Math.Sqrt(am1 * (a + 1.0)));
                    }
                    //Debug.Assert(am1 >= 0.0);
                }
                else
                {
                    // Because of the test above, we can be sure that a * a will not overflow.
                    v = Math.Log(a + Math.Sqrt((a - 1.0) * (a + 1.0)));
                }
            }
        }
Пример #5
0
        // basic functions of complex arguments

        /// <summary>
        /// Computes the absolute value of a complex number.
        /// </summary>
        /// <param name="z">The argument.</param>
        /// <returns>The value of |z|.</returns>
        /// <remarks>
        /// <para>The absolute value of a complex number is the distance of the number from the origin
        /// in the complex plane. This is a compatible generalization of the definition of the absolute
        /// value of a real number.</para>
        /// </remarks>
        /// <seealso cref="Math.Abs(Double)"/>
        public static double Abs(Complex z)
        {
            return(MoreMath.Hypot(z.Re, z.Im));
        }
Пример #6
0
        /// <summary>
        /// Computes the square root of a complex number.
        /// </summary>
        /// <param name="z">The argument.</param>
        /// <returns>The square root of the argument.</returns>
        /// <remarks>
        /// <para>The image below shows the complex square root function near the origin, using domain coloring.</para>
        /// <img src="../images/ComplexSqrtPlot.png" />
        /// <para>You can see the branch cut extending along the negative real axis from the zero at the origin.</para>
        /// </remarks>
        public static Complex Sqrt(Complex z)
        {
            // Handle the degenerate case quickly.
            if (z.Im == 0.0)
            {
                if (z.Re < 0.0)
                {
                    return(new Complex(0.0, Math.Sqrt(-z.Re)));
                }
                else
                {
                    return(new Complex(Math.Sqrt(z.Re), 0.0));
                }
            }
            // This also eliminates need to worry about Im(z) = 0 in subsequent formulas.

            // One way to compute Sqrt(z) is just to call Pow(z, 0.5), which coverts to polar coordinates
            // (sqrt + atan), halves the phase, and reconverts to cartesian coordinates (cos + sin).
            // Not only is this more expensive than necessary, it also fails to preserve certain expected
            // symmetries, such as that the square root of a pure negative is a pure imaginary, and that the
            // square root of a pure imaginary has exactly equal real and imaginary parts. This all goes
            // back to the fact that Math.PI is not stored with infinite precision, so taking half of Math.PI
            // does not land us on an argument with sine exactly equal to zero.

            // To find a fast and symmetry-respecting formula for complex square root,
            // note x + i y = \sqrt{a + i b} implies x^2 + 2 i x y - y^2 = a + i b,
            // so x^2 - y^2 = a and 2 x y = b. Cross-substitute and use the quadratic formula to obtain
            //   x = \sqrt{\frac{\sqrt{a^2 + b^2} + a}{2}}  y = \pm \sqrt{\frac{\sqrt{a^2 + b^2} - a}{2}}
            // There is just one complication: depending on the sign on a, either x or y suffers from
            // cancelation when |b| << |a|. We can get aroud this by noting that our formulas imply
            // x^2 y^2 = b^2 / 4, so |x| |y| = |b| / 2. So after computing the one that doesn't suffer
            // from cancelation, we can compute the other with just a division. This is basically just
            // the right way to evaluate the quadratic formula without cancelation.

            // All this reduces our total cost to two sqrts and a few flops, and it respects the desired
            // symmetries. Much better than atan + cos + sin!

            // The signs are a matter of choice of branch cut, which is traditionally taken so x > 0 and sign(y) = sign(b).

            double a = z.Re;
            double b = z.Im;

            // If the components are too large, Hypot(a, b) will overflow, even though the subsequent sqrt would
            // make the result representable. To avoid this, we re-scale (by exact powers of 2 for accuracy)
            // when we encounter very large components to avoid intermediate infinities.
            bool rescale = false;

            if ((Math.Abs(a) >= sqrtRescaleThreshold) || (Math.Abs(b) >= sqrtRescaleThreshold))
            {
                if (Double.IsInfinity(b) && !Double.IsNaN(a))
                {
                    return(new Complex(Double.PositiveInfinity, b));
                }
                a      *= 0.25;
                b      *= 0.25;
                rescale = true;
            }
            // 1. Note that, if one component is very large and the other is very small, this re-scale could cause
            // the very small component to underflow. This is not a problem, though, because the neglected term
            // is ~ (very small) / (very large), which would underflow anyway.
            // 2. Note that our test for re-scaling requires two abs and two floating point comparisons.
            // For the typical non-overflowng case, it might be faster to just test for IsInfinity after Hypot,
            // then re-scale and re-do Hypot if overflow is detected. That version makes the code more
            // complicated and simple profiling experiments show no discernible perf improvement, so
            // stick with this version for now.

            double x, y;

            if (a >= 0.0)
            {
                x = Math.Sqrt((MoreMath.Hypot(a, b) + a) / 2.0);
                y = b / (2.0 * x);
            }
            else
            {
                y = Math.Sqrt((MoreMath.Hypot(a, b) - a) / 2.0);
                if (b < 0.0)
                {
                    y = -y;
                }
                x = b / (2.0 * y);
            }
            // Note that, because we have re-scaled when any components are very large,
            // (2.0 * x) and (2.0 * y) are guaranteed not to overflow.

            if (rescale)
            {
                x *= 2.0;
                y *= 2.0;
            }


            return(new Complex(x, y));
        }
Пример #7
0
 /// <summary>
 /// Subtracts two uncertain values.
 /// </summary>
 /// <param name="v1">The first uncertain value.</param>
 /// <param name="v2">The second uncertain value.</param>
 /// <returns>The difference of the two uncertain values.</returns>
 public static UncertainValue operator-(UncertainValue v1, UncertainValue v2)
 {
     return(new UncertainValue(v1.Value - v2.Value, MoreMath.Hypot(v1.Uncertainty, v2.Uncertainty)));
 }
Пример #8
0
        /// <summary>
        /// Computes the square root of a complex number.
        /// </summary>
        /// <param name="z">The argument.</param>
        /// <returns>The square root of the argument.</returns>
        /// <remarks>
        /// <para>The image below shows the complex square root function near the origin, using domain coloring.</para>
        /// <img src="../images/ComplexSqrtPlot.png" />
        /// <para>You can see the branch cut extending along the negative real axis from the zero at the origin.</para>
        /// </remarks>
        public static Complex Sqrt(Complex z)
        {
            if (z.Im == 0.0)
            {
                // Handle the degenerate case quickly.
                // This also eliminates need to worry about Im(z) = 0 in subsequent formulas.
                if (z.Re < 0.0)
                {
                    return(new Complex(0.0, Math.Sqrt(-z.Re)));
                }
                else
                {
                    return(new Complex(Math.Sqrt(z.Re), 0.0));
                }
            }
            else
            {
                // To find a fast formula for complex square root, note x + i y = \sqrt{a + i b} implies
                // x^2 + 2 i x y - y^2 = a + i b, so x^2 - y^2 = a and 2 x y = b. Cross-substitute and
                // use quadratic formula to solve for x^2 and y^2 to obtain
                //  x = \sqrt{\frac{\sqrt{a^2 + b^2} + a}{2}}  y = \pm \sqrt{\frac{\sqrt{a^2 + b^2} - a}{2}}
                // This gives complex square root in three square roots and a few flops.

                // Only problem is b << a case, where significant cancelation occurs in one of the formula.
                // (Which one depends on the sign of a.) Handle that case by series expansion.

                double p, q;
                if (Math.Abs(z.Im) < 0.25 * Math.Abs(z.Re))
                {
                    double x2 = MoreMath.Sqr(z.Im / z.Re);
                    double t  = x2 / 2.0;
                    double s  = t;
                    // Find s = \sqrt{1 + x^2} - 1 using binomial expansion
                    for (int k = 2; true; k++)
                    {
                        if (k > Global.SeriesMax)
                        {
                            throw new NonconvergenceException();
                        }
                        double s_old = s;
                        t *= (1.5 / k - 1.0) * x2;
                        s += t;
                        if (s == s_old)
                        {
                            break;
                        }
                    }
                    if (z.Re < 0.0)
                    {
                        p = -z.Re * s;
                        q = -2.0 * z.Re + p;
                    }
                    else
                    {
                        q = z.Re * s;
                        p = 2.0 * z.Re + q;
                    }
                }
                else
                {
                    double m = ComplexMath.Abs(z);
                    p = m + z.Re;
                    q = m - z.Re;
                }

                double x = Math.Sqrt(p / 2.0);
                double y = Math.Sqrt(q / 2.0);
                if (z.Im < 0.0)
                {
                    y = -y;
                }
                return(new Complex(x, y));

                /*
                 * if (Math.Abs(z.Im) < 0.125 * Math.Abs(z.Re)) {
                 *  // We should try to improve this by using a series instead of the full power algorithm.
                 *  return (Pow(z, 0.5));
                 * } else {
                 *  // This is a pretty fast formula for a complex square root, basically just
                 *  // three square roots and a few flops.
                 *  // But if z.Im << z.Re, then z.Re ~ m and it suffers from cancelations.
                 *  double m = Abs(z);
                 *  double x = Math.Sqrt((m + z.Re) / 2.0);
                 *  double y = Math.Sqrt((m - z.Re) / 2.0);
                 *  if (z.Im < 0.0) y = -y;
                 *  return (new Complex(x, y));
                 * }
                 */
            }
        }