/// <summary> /// Computes the sine of the given value to full significance over the full range of arguments. /// </summary> /// <param name="x">The argument.</param> /// <returns>The value of sin(x).</returns> /// <remarks> /// <para>This method addresses several subtle shortcomings of the <see cref="System.Math.Sin" /> method. /// One shortcoming, quite striking but rarely encountered, is that <see cref="Math.Sin"/> returns entirely wrong results very large /// arguments -- for x larger than about 10<sup>20</sup>, it simply returns the argument as the function value! /// (I have no idea /// why the base class library designers did not at least choose to return <see cref="Double.NaN"/>, so as to signal to the caller /// that the result should not be trusted. No floating point standard specifies this crazy behavior.) /// Another shortcoming, more commonly encountered but often unnoticed, is that for large but not necessarily very large arguments, /// function values loose precision, particularly near zeros of the function.</para> /// <para> /// One way to view these shortcomings is that they are justified by the uncertainty inherent in floating point representations. In this /// view, any <see cref="System.Double"/> should be seen as an uncertain value with a relative error of ~10<sup>-16</sup>. If the /// argument is large enough, then the absolute size of this error can be as large or larger than 2π; in this circumstance we should not /// expect to be able to say anything about the value (except, of course, that it is between -1 and +1, which is violated by the designers' crazy /// choice to return the argument as the value). Even if the absolute error is just a non-negligible faction of 2π, there is a non-negligible fraction /// of the values between -1 and +1 in the corresponding range of function values; any of these values is as possible as any other as a value /// for the sine of our uncertain argument, so we should be satisfied with any returned value in this non-negligible range. /// </para> /// <para>A different view is that it is better to regard every representable floating point value as some exact rational number, and /// when computing functions of floating point numbers, we should strive to return the representable floating point value nearest /// to the actual function value for that exact rational. /// Callers are unlikely to complain if we are careful in this regard, and this behavior is particularly /// useful when the argument is an intermediate result that the programmer may not even realize has become large. /// Thus is the view that we adopt, and therefore we provide this improved trigonometric function.</para> /// <para>For typical arguments, say between -10<sup>4</sup> and 10<sup>4</sup>, the extra cost of calling this function instead of /// <see cref="Math.Sin"/> is just a couple of comparisons and a single floating point operation; less than 0.1% of /// arguments in this range are then routed to our much slower, high-accuracy algorithm. We therefore suggest that, /// for general use, you prefer this method over <see cref="Math.Sin"/>; only in very unusual situations where (i) you are guaranteed /// never to encounter very large arguments, (ii) full precision values are not required, and (iii) the run-time of your /// application is critical and dominated by trigonometric calculations, should you prefer the base class library method.</para> /// </remarks> public static double Sin(double x) { if (x < 0.0) { return(-Sin(-x)); } else if (x < 1.0) { // If x is small enough not to cross a zero, use the built-in function return(Math.Sin(x)); } else if (x < RangeReduction.xLimit) { // If x is in the intermediate region, try the built-in function but switch to our range-reduction // algorithm if the result is too small. double y = Math.Sin(x); double ym = x / RangeReduction.xLimit; if (Math.Abs(y) < ym) { return(RangeReduction.Sin(x)); } else { return(y); } } else if (x < Double.PositiveInfinity) { // If x is beyond the range of the built-in function entirely, use our range-reduction algorithm return(RangeReduction.Sin(x)); } else { // If x is infinite or NaN, return NaN. return(Double.NaN); } }
/// <summary> /// Computes the cosine of the given multiple of π. /// </summary> /// <param name="x">The argument.</param> /// <returns>The value of cos(<paramref name="x"/>π).</returns> /// <remarks> /// <para>For an explanation of why and when to use this function, /// see <see cref="SinPi(double)"/>.</para> /// </remarks> /// <seealso cref="SinPi(double)"/> public static double CosPi(double x) { RangeReduction.ReduceByOnes(2.0 * x, out long y0, out double y1); return(RangeReduction.Cos(y0, y1)); }