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(); } }
/// <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)); } }
/// <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))); }
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))); } } }
// 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)); }
/// <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)); }
/// <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))); }
/// <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)); * } */ } }