File: src\libraries\System.Private.CoreLib\src\System\Numerics\ITrigonometricFunctions.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
namespace System.Numerics
{
    /// <summary>Defines support for trigonometric functions.</summary>
    /// <typeparam name="TSelf">The type that implements this interface.</typeparam>
    public interface ITrigonometricFunctions<TSelf>
        : IFloatingPointConstants<TSelf>
        where TSelf : ITrigonometricFunctions<TSelf>?
    {
        /// <summary>Computes the arc-cosine of a value.</summary>
        /// <param name="x">The value whose arc-cosine is to be computed.</param>
        /// <returns>The arc-cosine of <paramref name="x" />.</returns>
        /// <remarks>This computes <c>arccos(x)</c> in the interval <c>[+0, +PI]</c> radians.</remarks>
        static abstract TSelf Acos(TSelf x);
 
        /// <summary>Computes the arc-cosine of a value and divides the result by <c>pi</c>.</summary>
        /// <param name="x">The value whose arc-cosine is to be computed.</param>
        /// <returns>The arc-cosine of <paramref name="x" />, divided by <c>pi</c>.</returns>
        /// <remarks>This computes <c>arccos(x) / PI</c> in the interval <c>[-0.5, +0.5]</c>.</remarks>
        static abstract TSelf AcosPi(TSelf x);
 
        /// <summary>Computes the arc-sine of a value.</summary>
        /// <param name="x">The value whose arc-sine is to be computed.</param>
        /// <returns>The arc-sine of <paramref name="x" />.</returns>
        /// <remarks>This computes <c>arcsin(x)</c> in the interval <c>[-PI / 2, +PI / 2]</c> radians.</remarks>
        static abstract TSelf Asin(TSelf x);
 
        /// <summary>Computes the arc-sine of a value and divides the result by <c>pi</c>.</summary>
        /// <param name="x">The value whose arc-sine is to be computed.</param>
        /// <returns>The arc-sine of <paramref name="x" />, divided by <c>pi</c>.</returns>
        /// <remarks>This computes <c>arcsin(x) / PI</c> in the interval <c>[-0.5, +0.5]</c>.</remarks>
        static abstract TSelf AsinPi(TSelf x);
 
        /// <summary>Computes the arc-tangent of a value.</summary>
        /// <param name="x">The value whose arc-tangent is to be computed.</param>
        /// <returns>The arc-tangent of <paramref name="x" />.</returns>
        /// <remarks>This computes <c>arctan(x)</c> in the interval <c>[-PI / 2, +PI / 2]</c> radians.</remarks>
        static abstract TSelf Atan(TSelf x);
 
        /// <summary>Computes the arc-tangent of a value and divides the result by pi.</summary>
        /// <param name="x">The value whose arc-tangent is to be computed.</param>
        /// <returns>The arc-tangent of <paramref name="x" />, divided by <c>pi</c>.</returns>
        /// <remarks>This computes <c>arctan(x) / PI</c> in the interval <c>[-0.5, +0.5]</c>.</remarks>
        static abstract TSelf AtanPi(TSelf x);
 
        /// <summary>Computes the cosine of a value.</summary>
        /// <param name="x">The value, in radians, whose cosine is to be computed.</param>
        /// <returns>The cosine of <paramref name="x" />.</returns>
        /// <remarks>This computes <c>cos(x)</c>.</remarks>
        static abstract TSelf Cos(TSelf x);
 
        /// <summary>Computes the cosine of a value that has been multipled by <c>pi</c>.</summary>
        /// <param name="x">The value, in half-revolutions, whose cosine is to be computed.</param>
        /// <returns>The cosine of <paramref name="x" /> multiplied-by <c>pi</c>.</returns>
        /// <remarks>This computes <c>cos(x * PI)</c>.</remarks>
        static abstract TSelf CosPi(TSelf x);
 
        /// <summary>Converts a given value from degrees to radians.</summary>
        /// <param name="degrees">The value to convert to radians.</param>
        /// <returns>The value of <paramref name="degrees" /> converted to radians.</returns>
        static virtual TSelf DegreesToRadians(TSelf degrees)
        {
            // We don't want to simplify this to: degrees * (Pi / 180)
            //
            // Doing so will change the result and in many cases will
            // cause a loss of precision. The main exception to this
            // is when the initial multiplication causes overflow, but
            // if we decide that should be handled in the future it needs
            // to be more explicit around how its done.
            //
            // Floating-point operations are naturally imprecise due to
            // rounding required to fit the "infinitely-precise result"
            // into the limits of the underlying representation. Because
            // of this, every operation can introduce some amount of rounding
            // error.
            //
            // For integers, the IEEE 754 binary floating-point types can
            // exactly represent them up to the 2^n, where n is the number
            // of bits in the significand. This is 10 for Half, 23 for Single,
            // and 52 for Double. As you approach this limit, the number of
            // digits available to represent the fractional portion decreases.
            //
            // For Half, you get around 3.311 total decimal digits of precision.
            // For Single, this is around 7.225 and around 15.955 for Double.
            //
            // The actual number of digits can be slightly more or less, depending.
            //
            // This means that values such as `Pi` are not exactly `Pi`, instead:
            // * Half:   3.14 0625
            // * Single: 3.14 1592 741012573 2421875
            // * Double: 3.14 1592 653589793 115997963468544185161590576171875
            // * Actual: 3.14 1592 653589793 2384626433832795028841971693993751058209749445923...
            //
            // If we were to simplify this to simply multiply by (Pi / 180), we get:
            // * Half:   0.01745 6054 6875
            // * Single: 0.01745 3292 384743690 49072265625
            // * Double: 0.01745 3292 519943295 4743716805978692718781530857086181640625
            // * Actual: 0.01745 3292 519943295 7692369076848861271344287188854172545609719144...
            //
            // Neither of these end up "perfect". There will be some cases where they will trade
            // in terms of closeness to the "infinitely precise result". Over the entire domain
            // however, doing the separate multiplications tends to produce overall more accurate
            // results. It helps ensure the implementation can be trivial for the DIM case, and
            // covers the vast majority of typical inputs more efficiently largely only pessimizing
            // the case where the first multiplication results in overflow.
            //
            // This is particularly true for `RadiansToDegrees` where 180 is exactly representable
            // and so allows an exactly representable intermediate value to be computed when overflow
            // doesn't occur.
 
            return (degrees * TSelf.Pi) / TSelf.CreateChecked(180);
        }
 
        /// <summary>Converts a given value from radians to degrees.</summary>
        /// <param name="radians">The value to convert to degrees.</param>
        /// <returns>The value of <paramref name="radians" /> converted to degrees.</returns>
        static virtual TSelf RadiansToDegrees(TSelf radians)
        {
            // We don't want to simplify this to: radians * (180 / Pi)
            // See DegreesToRadians for a longer explanation as to why
 
            return (radians * TSelf.CreateChecked(180)) / TSelf.Pi;
        }
 
        /// <summary>Computes the sine of a value.</summary>
        /// <param name="x">The value, in radians, whose sine is to be computed.</param>
        /// <returns>The sine of <paramref name="x" />.</returns>
        /// <remarks>This computes <c>sin(x)</c>.</remarks>
        static abstract TSelf Sin(TSelf x);
 
        /// <summary>Computes the sine and cosine of a value.</summary>
        /// <param name="x">The value, in radians, whose sine and cosine are to be computed.</param>
        /// <returns>The sine and cosine of <paramref name="x" />.</returns>
        /// <remarks>This computes <c>(sin(x), cos(x))</c>.</remarks>
        static abstract (TSelf Sin, TSelf Cos) SinCos(TSelf x);
 
        /// <summary>Computes the sine and cosine of a value that has been multiplied by <c>pi</c>.</summary>
        /// <param name="x">The value, in half-revolutions, that is multipled by <c>pi</c> before computing its sine and cosine.</param>
        /// <returns>The sine and cosine of<paramref name="x" /> multiplied-by <c>pi</c>.</returns>
        /// <remarks>This computes <c>(sin(x * PI), cos(x * PI))</c>.</remarks>
        static abstract (TSelf SinPi, TSelf CosPi) SinCosPi(TSelf x);
 
        /// <summary>Computes the sine of a value that has been multiplied by <c>pi</c>.</summary>
        /// <param name="x">The value, in half-revolutions, that is multipled by <c>pi</c> before computing its sine.</param>
        /// <returns>The sine of <paramref name="x" /> multiplied-by <c>pi</c>.</returns>
        /// <remarks>This computes <c>sin(x * PI)</c>.</remarks>
        static abstract TSelf SinPi(TSelf x);
 
        /// <summary>Computes the tangent of a value.</summary>
        /// <param name="x">The value, in radians, whose tangent is to be computed.</param>
        /// <returns>The tangent of <paramref name="x" />.</returns>
        /// <remarks>This computes <c>tan(x)</c>.</remarks>
        static abstract TSelf Tan(TSelf x);
 
        /// <summary>Computes the tangent of a value that has been multipled by <c>pi</c>.</summary>
        /// <param name="x">The value, in half-revolutions, that is multipled by <c>pi</c> before computing its tangent.</param>
        /// <returns>The tangent of <paramref name="x" /> multiplied-by <c>pi</c>.</returns>
        /// <remarks>This computes <c>tan(x * PI)</c>.</remarks>
        static abstract TSelf TanPi(TSelf x);
    }
}