/// <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 shortcommings 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 user /// that the result should not be trusted. No floating point standard specifies this crazy behavior.) /// Another shortcomming, 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 shortcommings 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 /// number is very large, 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-negligable faction of 2π, there is a non-neglible fraction /// of the values between -1 and +1 in the corresponding range of function values; any of these values is as good as any other as an answer to the /// question of the sine of our uncertain argument, so we should be satisfied with any returned value in this non-negligible range. /// </para> /// <para>A contrary view is that it is better to treat all arguments as infinitely precise and return the nearest representable <see cref="System.Double"/> /// to the actual function value under this assumption. Users are unlikely to complain that we returned a more accurate value and this behavior is particularly /// useful when the argument is an intermediate result that the programmer may not even realize has become large.</para> /// <para>For typical arguments, say between 10<sup>-4</sup> and 10<sup>4</sup>, the extra cost of this function over /// <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, higher-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 is beyond the range of the built-in function eintirely, use our range-reduction algorithm return(RangeReduction.Sin(x)); } }
internal static double SinPi(double x) { long y0; double y1; RangeReduction.ReduceByOnes(2.0 * x, out y0, out y1); return(RangeReduction.Sin(y0, y1)); }
/// <summary> /// Computes the sine of the given multiple of π. /// </summary> /// <param name="x">The argument.</param> /// <returns>The value of sin(<paramref name="x"/>π).</returns> /// <remarks> /// <para>This function allows the user to increase performance and avoid inaccuracies due to the finite /// precision of the stored constant <see cref="Math.PI"/> in some cases. Suppose you need to compute /// sin(xπ) for a large value of x. /// </para> /// </remarks> /// <seealso cref="CosPi(double)"/> public static double SinPi(double x) { RangeReduction.ReduceByOnes(2.0 * x, out long y0, out double y1); return(RangeReduction.Sin(y0, y1)); }