// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; namespace System { /// <summary> /// Represents a double-precision floating-point number. /// </summary> [Serializable] [StructLayout(LayoutKind.Sequential)] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct Double : IComparable, IConvertible, ISpanFormattable, IComparable<double>, IEquatable<double>, IBinaryFloatingPointIeee754<double>, IMinMaxValue<double>, IUtf8SpanFormattable, IBinaryFloatParseAndFormatInfo<double> { private readonly double m_value; // Do not rename (binary serialization) // // Public Constants // public const double MinValue = -1.7976931348623157E+308; public const double MaxValue = 1.7976931348623157E+308; // Note Epsilon should be a double whose hex representation is 0x1 // on little endian machines. public const double Epsilon = 4.9406564584124654E-324; public const double NegativeInfinity = (double)-1.0 / (double)(0.0); public const double PositiveInfinity = (double)1.0 / (double)(0.0); public const double NaN = (double)0.0 / (double)0.0; /// <summary>Represents the additive identity (0).</summary> internal const double AdditiveIdentity = 0.0; /// <summary>Represents the multiplicative identity (1).</summary> internal const double MultiplicativeIdentity = 1.0; /// <summary>Represents the number one (1).</summary> internal const double One = 1.0; /// <summary>Represents the number zero (0).</summary> internal const double Zero = 0.0; /// <summary>Represents the number negative one (-1).</summary> internal const double NegativeOne = -1.0; /// <summary>Represents the number negative zero (-0).</summary> public const double NegativeZero = -0.0; /// <summary>Represents the natural logarithmic base, specified by the constant, e.</summary> /// <remarks>Euler's number is approximately 2.7182818284590452354.</remarks> public const double E = Math.E; /// <summary>Represents the ratio of the circumference of a circle to its diameter, specified by the constant, PI.</summary> /// <remarks>Pi is approximately 3.1415926535897932385.</remarks> public const double Pi = Math.PI; /// <summary>Represents the number of radians in one turn, specified by the constant, Tau.</summary> /// <remarks>Tau is approximately 6.2831853071795864769.</remarks> public const double Tau = Math.Tau; // // Constants for manipulating the private bit-representation // internal const ulong SignMask = 0x8000_0000_0000_0000; internal const int SignShift = 63; internal const byte ShiftedSignMask = (byte)(SignMask >> SignShift); internal const ulong BiasedExponentMask = 0x7FF0_0000_0000_0000; internal const int BiasedExponentShift = 52; internal const int BiasedExponentLength = 11; internal const ushort ShiftedBiasedExponentMask = (ushort)(BiasedExponentMask >> BiasedExponentShift); internal const ulong TrailingSignificandMask = 0x000F_FFFF_FFFF_FFFF; internal const byte MinSign = 0; internal const byte MaxSign = 1; internal const ushort MinBiasedExponent = 0x0000; internal const ushort MaxBiasedExponent = 0x07FF; internal const ushort ExponentBias = 1023; internal const short MinExponent = -1022; internal const short MaxExponent = +1023; internal const ulong MinTrailingSignificand = 0x0000_0000_0000_0000; internal const ulong MaxTrailingSignificand = 0x000F_FFFF_FFFF_FFFF; internal const int TrailingSignificandLength = 52; internal const int SignificandLength = TrailingSignificandLength + 1; // Constants representing the private bit-representation for various default values internal const ulong PositiveZeroBits = 0x0000_0000_0000_0000; internal const ulong NegativeZeroBits = 0x8000_0000_0000_0000; internal const ulong EpsilonBits = 0x0000_0000_0000_0001; internal const ulong PositiveInfinityBits = 0x7FF0_0000_0000_0000; internal const ulong NegativeInfinityBits = 0xFFF0_0000_0000_0000; internal const ulong SmallestNormalBits = 0x0010_0000_0000_0000; internal ushort BiasedExponent { get { ulong bits = BitConverter.DoubleToUInt64Bits(m_value); return ExtractBiasedExponentFromBits(bits); } } internal short Exponent { get { return (short)(BiasedExponent - ExponentBias); } } internal ulong Significand { get { return TrailingSignificand | ((BiasedExponent != 0) ? (1UL << BiasedExponentShift) : 0UL); } } internal ulong TrailingSignificand { get { ulong bits = BitConverter.DoubleToUInt64Bits(m_value); return ExtractTrailingSignificandFromBits(bits); } } internal static ushort ExtractBiasedExponentFromBits(ulong bits) { return (ushort)((bits >> BiasedExponentShift) & ShiftedBiasedExponentMask); } internal static ulong ExtractTrailingSignificandFromBits(ulong bits) { return bits & TrailingSignificandMask; } internal static double CreateDouble(bool sign, ushort exp, ulong sig) => BitConverter.UInt64BitsToDouble((sign ? SignMask : 0UL) + ((ulong)exp << BiasedExponentShift) + sig); /// <summary>Determines whether the specified value is finite (zero, subnormal, or normal).</summary> /// <remarks>This effectively checks the value is not NaN and not infinite.</remarks> [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsFinite(double d) { ulong bits = BitConverter.DoubleToUInt64Bits(d); return (~bits & PositiveInfinityBits) != 0; } /// <summary>Determines whether the specified value is infinite.</summary> [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsInfinity(double d) { ulong bits = BitConverter.DoubleToUInt64Bits(Abs(d)); return bits == PositiveInfinityBits; } /// <summary>Determines whether the specified value is NaN.</summary> [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNaN(double d) { // A NaN will never equal itself so this is an // easy and efficient way to check for NaN. #pragma warning disable CS1718 return d != d; #pragma warning restore CS1718 } [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsNaNOrZero(double d) { ulong bits = BitConverter.DoubleToUInt64Bits(d); return ((bits - 1) & ~SignMask) >= PositiveInfinityBits; } /// <summary>Determines whether the specified value is negative.</summary> [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNegative(double d) { return BitConverter.DoubleToInt64Bits(d) < 0; } /// <summary>Determines whether the specified value is negative infinity.</summary> [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNegativeInfinity(double d) { return d == NegativeInfinity; } /// <summary>Determines whether the specified value is normal (finite, but not zero or subnormal).</summary> /// <remarks>This effectively checks the value is not NaN, not infinite, not subnormal, and not zero.</remarks> [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNormal(double d) { ulong bits = BitConverter.DoubleToUInt64Bits(Abs(d)); return (bits - SmallestNormalBits) < (PositiveInfinityBits - SmallestNormalBits); } /// <summary>Determines whether the specified value is positive infinity.</summary> [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsPositiveInfinity(double d) { return d == PositiveInfinity; } /// <summary>Determines whether the specified value is subnormal (finite, but not zero or normal).</summary> /// <remarks>This effectively checks the value is not NaN, not infinite, not normal, and not zero.</remarks> [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsSubnormal(double d) { ulong bits = BitConverter.DoubleToUInt64Bits(Abs(d)); return (bits - 1) < MaxTrailingSignificand; } [NonVersionable] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsZero(double d) { return d == 0; } // Compares this object to another object, returning an instance of System.Relation. // Null is considered less than any instance. // // If object is not of type Double, this method throws an ArgumentException. // // Returns a value less than zero if this object // public int CompareTo(object? value) { if (value is not double other) { return (value is null) ? 1 : throw new ArgumentException(SR.Arg_MustBeDouble); } return CompareTo(other); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(double value) { if (m_value < value) { return -1; } if (m_value > value) { return 1; } if (m_value == value) { return 0; } if (IsNaN(m_value)) { return IsNaN(value) ? 0 : -1; } Debug.Assert(IsNaN(value)); return 1; } // True if obj is another Double with the same value as the current instance. This is // a method of object equality, that only returns true if obj is also a double. public override bool Equals([NotNullWhen(true)] object? obj) { return (obj is double other) && Equals(other); } /// <inheritdoc cref="IEqualityOperators{TSelf, TOther, TResult}.op_Equality(TSelf, TOther)" /> [NonVersionable] public static bool operator ==(double left, double right) => left == right; /// <inheritdoc cref="IEqualityOperators{TSelf, TOther, TResult}.op_Inequality(TSelf, TOther)" /> [NonVersionable] public static bool operator !=(double left, double right) => left != right; /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_LessThan(TSelf, TOther)" /> [NonVersionable] public static bool operator <(double left, double right) => left < right; /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThan(TSelf, TOther)" /> [NonVersionable] public static bool operator >(double left, double right) => left > right; /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_LessThanOrEqual(TSelf, TOther)" /> [NonVersionable] public static bool operator <=(double left, double right) => left <= right; /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThanOrEqual(TSelf, TOther)" /> [NonVersionable] public static bool operator >=(double left, double right) => left >= right; public bool Equals(double obj) { if (obj == m_value) { return true; } return IsNaN(obj) && IsNaN(m_value); } // The hashcode for a double is the absolute value of the integer representation // of that double. [MethodImpl(MethodImplOptions.AggressiveInlining)] // 64-bit constants make the IL unusually large that makes the inliner to reject the method public override int GetHashCode() { ulong bits = BitConverter.DoubleToUInt64Bits(m_value); if (IsNaNOrZero(m_value)) { // Ensure that all NaNs and both zeros have the same hash code bits &= PositiveInfinityBits; } return unchecked((int)bits) ^ ((int)(bits >> 32)); } public override string ToString() { return Number.FormatFloat(m_value, null, NumberFormatInfo.CurrentInfo); } public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) { return Number.FormatFloat(m_value, format, NumberFormatInfo.CurrentInfo); } public string ToString(IFormatProvider? provider) { return Number.FormatFloat(m_value, null, NumberFormatInfo.GetInstance(provider)); } public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider) { return Number.FormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider)); } public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null) { return Number.TryFormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } /// <inheritdoc cref="IUtf8SpanFormattable.TryFormat" /> public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null) { return Number.TryFormatFloat(m_value, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); } public static double Parse(string s) => Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider: null); public static double Parse(string s, NumberStyles style) => Parse(s, style, provider: null); public static double Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider); public static double Parse(string s, NumberStyles style, IFormatProvider? provider) { if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } // Parses a double from a String in the given style. If // a NumberFormatInfo isn't specified, the current culture's // NumberFormatInfo is assumed. // // This method will not throw an OverflowException, but will return // PositiveInfinity or NegativeInfinity for a number that is too // large or too small. public static double Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Float | NumberStyles.AllowThousands, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); return Number.ParseFloat<char, double>(s, style, NumberFormatInfo.GetInstance(provider)); } public static bool TryParse([NotNullWhen(true)] string? s, out double result) => TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider: null, out result); public static bool TryParse(ReadOnlySpan<char> s, out double result) => TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider: null, out result); /// <summary>Tries to convert a UTF-8 character span containing the string representation of a number to its double-precision floating-point number equivalent.</summary> /// <param name="utf8Text">A read-only UTF-8 character span that contains the number to convert.</param> /// <param name="result">When this method returns, contains a double-precision floating-point number equivalent of the numeric value or symbol contained in <paramref name="utf8Text" /> if the conversion succeeded or zero if the conversion failed. The conversion fails if the <paramref name="utf8Text" /> is <see cref="ReadOnlySpan{T}.Empty" /> or is not in a valid format. This parameter is passed uninitialized; any value originally supplied in result will be overwritten.</param> /// <returns><c>true</c> if <paramref name="utf8Text" /> was converted successfully; otherwise, false.</returns> public static bool TryParse(ReadOnlySpan<byte> utf8Text, out double result) => TryParse(utf8Text, NumberStyles.Float | NumberStyles.AllowThousands, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out double result) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _); } public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out double result) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result, out _); } // // IConvertible implementation // public TypeCode GetTypeCode() { return TypeCode.Double; } bool IConvertible.ToBoolean(IFormatProvider? provider) { return Convert.ToBoolean(m_value); } char IConvertible.ToChar(IFormatProvider? provider) { throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Double", "Char")); } sbyte IConvertible.ToSByte(IFormatProvider? provider) { return Convert.ToSByte(m_value); } byte IConvertible.ToByte(IFormatProvider? provider) { return Convert.ToByte(m_value); } short IConvertible.ToInt16(IFormatProvider? provider) { return Convert.ToInt16(m_value); } ushort IConvertible.ToUInt16(IFormatProvider? provider) { return Convert.ToUInt16(m_value); } int IConvertible.ToInt32(IFormatProvider? provider) { return Convert.ToInt32(m_value); } uint IConvertible.ToUInt32(IFormatProvider? provider) { return Convert.ToUInt32(m_value); } long IConvertible.ToInt64(IFormatProvider? provider) { return Convert.ToInt64(m_value); } ulong IConvertible.ToUInt64(IFormatProvider? provider) { return Convert.ToUInt64(m_value); } float IConvertible.ToSingle(IFormatProvider? provider) { return Convert.ToSingle(m_value); } double IConvertible.ToDouble(IFormatProvider? provider) { return m_value; } decimal IConvertible.ToDecimal(IFormatProvider? provider) { return Convert.ToDecimal(m_value); } DateTime IConvertible.ToDateTime(IFormatProvider? provider) { throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Double", "DateTime")); } object IConvertible.ToType(Type type, IFormatProvider? provider) { return Convert.DefaultToType((IConvertible)this, type, provider); } // // IAdditionOperators // /// <inheritdoc cref="IAdditionOperators{TSelf, TOther, TResult}.op_Addition(TSelf, TOther)" /> static double IAdditionOperators<double, double, double>.operator +(double left, double right) => left + right; // // IAdditiveIdentity // /// <inheritdoc cref="IAdditiveIdentity{TSelf, TResult}.AdditiveIdentity" /> static double IAdditiveIdentity<double, double>.AdditiveIdentity => AdditiveIdentity; // // IBinaryNumber // /// <inheritdoc cref="IBinaryNumber{TSelf}.AllBitsSet" /> static double IBinaryNumber<double>.AllBitsSet => BitConverter.UInt64BitsToDouble(0xFFFF_FFFF_FFFF_FFFF); /// <inheritdoc cref="IBinaryNumber{TSelf}.IsPow2(TSelf)" /> public static bool IsPow2(double value) { ulong bits = BitConverter.DoubleToUInt64Bits(value); if ((long)bits <= 0) { // Zero and negative values cannot be powers of 2 return false; } ushort biasedExponent = ExtractBiasedExponentFromBits(bits); ulong trailingSignificand = ExtractTrailingSignificandFromBits(bits); if (biasedExponent == MinBiasedExponent) { // Subnormal values have 1 bit set when they're powers of 2 return ulong.PopCount(trailingSignificand) == 1; } else if (biasedExponent == MaxBiasedExponent) { // NaN and Infinite values cannot be powers of 2 return false; } // Normal values have 0 bits set when they're powers of 2 return trailingSignificand == MinTrailingSignificand; } /// <inheritdoc cref="IBinaryNumber{TSelf}.Log2(TSelf)" /> [Intrinsic] public static double Log2(double value) => Math.Log2(value); // // IBitwiseOperators // /// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_BitwiseAnd(TSelf, TOther)" /> static double IBitwiseOperators<double, double, double>.operator &(double left, double right) { ulong bits = BitConverter.DoubleToUInt64Bits(left) & BitConverter.DoubleToUInt64Bits(right); return BitConverter.UInt64BitsToDouble(bits); } /// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_BitwiseOr(TSelf, TOther)" /> static double IBitwiseOperators<double, double, double>.operator |(double left, double right) { ulong bits = BitConverter.DoubleToUInt64Bits(left) | BitConverter.DoubleToUInt64Bits(right); return BitConverter.UInt64BitsToDouble(bits); } /// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_ExclusiveOr(TSelf, TOther)" /> static double IBitwiseOperators<double, double, double>.operator ^(double left, double right) { ulong bits = BitConverter.DoubleToUInt64Bits(left) ^ BitConverter.DoubleToUInt64Bits(right); return BitConverter.UInt64BitsToDouble(bits); } /// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_OnesComplement(TSelf)" /> static double IBitwiseOperators<double, double, double>.operator ~(double value) { ulong bits = ~BitConverter.DoubleToUInt64Bits(value); return BitConverter.UInt64BitsToDouble(bits); } // // IDecrementOperators // /// <inheritdoc cref="IDecrementOperators{TSelf}.op_Decrement(TSelf)" /> static double IDecrementOperators<double>.operator --(double value) => --value; // // IDivisionOperators // /// <inheritdoc cref="IDivisionOperators{TSelf, TOther, TResult}.op_Division(TSelf, TOther)" /> static double IDivisionOperators<double, double, double>.operator /(double left, double right) => left / right; // // IExponentialFunctions // /// <inheritdoc cref="IExponentialFunctions{TSelf}.Exp" /> [Intrinsic] public static double Exp(double x) => Math.Exp(x); /// <inheritdoc cref="IExponentialFunctions{TSelf}.ExpM1(TSelf)" /> public static double ExpM1(double x) => Math.Exp(x) - 1; /// <inheritdoc cref="IExponentialFunctions{TSelf}.Exp2(TSelf)" /> public static double Exp2(double x) => Math.Pow(2, x); /// <inheritdoc cref="IExponentialFunctions{TSelf}.Exp2M1(TSelf)" /> public static double Exp2M1(double x) => Math.Pow(2, x) - 1; /// <inheritdoc cref="IExponentialFunctions{TSelf}.Exp10(TSelf)" /> public static double Exp10(double x) => Math.Pow(10, x); /// <inheritdoc cref="IExponentialFunctions{TSelf}.Exp10M1(TSelf)" /> public static double Exp10M1(double x) => Math.Pow(10, x) - 1; // // IFloatingPoint // /// <inheritdoc cref="IFloatingPoint{TSelf}.Ceiling(TSelf)" /> [Intrinsic] public static double Ceiling(double x) => Math.Ceiling(x); /// <inheritdoc cref="IFloatingPoint{TSelf}.ConvertToInteger{TInteger}(TSelf)" /> [Intrinsic] public static TInteger ConvertToInteger<TInteger>(double value) where TInteger : IBinaryInteger<TInteger> => TInteger.CreateSaturating(value); /// <inheritdoc cref="IFloatingPoint{TSelf}.ConvertToIntegerNative{TInteger}(TSelf)" /> [Intrinsic] public static TInteger ConvertToIntegerNative<TInteger>(double value) where TInteger : IBinaryInteger<TInteger> { if (typeof(TInteger).IsPrimitive) { // We need this to be recursive so indirect calls (delegates // for example) produce the same result as direct invocation return ConvertToIntegerNative<TInteger>(value); } return TInteger.CreateSaturating(value); } /// <inheritdoc cref="IFloatingPoint{TSelf}.Floor(TSelf)" /> [Intrinsic] public static double Floor(double x) => Math.Floor(x); /// <inheritdoc cref="IFloatingPoint{TSelf}.Round(TSelf)" /> [Intrinsic] public static double Round(double x) => Math.Round(x); /// <inheritdoc cref="IFloatingPoint{TSelf}.Round(TSelf, int)" /> public static double Round(double x, int digits) => Math.Round(x, digits); /// <inheritdoc cref="IFloatingPoint{TSelf}.Round(TSelf, MidpointRounding)" /> public static double Round(double x, MidpointRounding mode) => Math.Round(x, mode); /// <inheritdoc cref="IFloatingPoint{TSelf}.Round(TSelf, int, MidpointRounding)" /> public static double Round(double x, int digits, MidpointRounding mode) => Math.Round(x, digits, mode); /// <inheritdoc cref="IFloatingPoint{TSelf}.Truncate(TSelf)" /> [Intrinsic] public static double Truncate(double x) => Math.Truncate(x); /// <inheritdoc cref="IFloatingPoint{TSelf}.GetExponentByteCount()" /> int IFloatingPoint<double>.GetExponentByteCount() => sizeof(short); /// <inheritdoc cref="IFloatingPoint{TSelf}.GetExponentShortestBitLength()" /> int IFloatingPoint<double>.GetExponentShortestBitLength() { short exponent = Exponent; if (exponent >= 0) { return (sizeof(short) * 8) - short.LeadingZeroCount(exponent); } else { return (sizeof(short) * 8) + 1 - short.LeadingZeroCount((short)(~exponent)); } } /// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandByteCount()" /> int IFloatingPoint<double>.GetSignificandByteCount() => sizeof(ulong); /// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandBitLength()" /> int IFloatingPoint<double>.GetSignificandBitLength() => 53; internal bool TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteInt16BigEndian(destination, Exponent)) { bytesWritten = sizeof(short); return true; } bytesWritten = 0; return false; } /// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteExponentBigEndian(Span{byte}, out int)" /> bool IFloatingPoint<double>.TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten) { return TryWriteExponentBigEndian(destination, out bytesWritten); } internal bool TryWriteExponentLittleEndian(Span<byte> destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteInt16LittleEndian(destination, Exponent)) { bytesWritten = sizeof(short); return true; } bytesWritten = 0; return false; } /// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteExponentLittleEndian(Span{byte}, out int)" /> bool IFloatingPoint<double>.TryWriteExponentLittleEndian(Span<byte> destination, out int bytesWritten) { return TryWriteExponentLittleEndian(destination, out bytesWritten); } internal bool TryWriteSignificandBigEndian(Span<byte> destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteUInt64BigEndian(destination, Significand)) { bytesWritten = sizeof(ulong); return true; } bytesWritten = 0; return false; } /// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteSignificandBigEndian(Span{byte}, out int)" /> bool IFloatingPoint<double>.TryWriteSignificandBigEndian(Span<byte> destination, out int bytesWritten) { return TryWriteSignificandBigEndian(destination, out bytesWritten); } internal bool TryWriteSignificandLittleEndian(Span<byte> destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteUInt64LittleEndian(destination, Significand)) { bytesWritten = sizeof(ulong); return true; } bytesWritten = 0; return false; } /// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteSignificandLittleEndian(Span{byte}, out int)" /> bool IFloatingPoint<double>.TryWriteSignificandLittleEndian(Span<byte> destination, out int bytesWritten) { return TryWriteSignificandLittleEndian(destination, out bytesWritten); } // // IFloatingPointConstants // /// <inheritdoc cref="IFloatingPointConstants{TSelf}.E" /> static double IFloatingPointConstants<double>.E => Math.E; /// <inheritdoc cref="IFloatingPointConstants{TSelf}.Pi" /> static double IFloatingPointConstants<double>.Pi => Pi; /// <inheritdoc cref="IFloatingPointConstants{TSelf}.Tau" /> static double IFloatingPointConstants<double>.Tau => Tau; // // IFloatingPointIeee754 // /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.Epsilon" /> static double IFloatingPointIeee754<double>.Epsilon => Epsilon; /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.NaN" /> static double IFloatingPointIeee754<double>.NaN => NaN; /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.NegativeInfinity" /> static double IFloatingPointIeee754<double>.NegativeInfinity => NegativeInfinity; /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.NegativeZero" /> static double IFloatingPointIeee754<double>.NegativeZero => NegativeZero; /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.PositiveInfinity" /> static double IFloatingPointIeee754<double>.PositiveInfinity => PositiveInfinity; /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.Atan2(TSelf, TSelf)" /> [Intrinsic] public static double Atan2(double y, double x) => Math.Atan2(y, x); /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.Atan2Pi(TSelf, TSelf)" /> public static double Atan2Pi(double y, double x) => Atan2(y, x) / Pi; /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.BitDecrement(TSelf)" /> public static double BitDecrement(double x) => Math.BitDecrement(x); /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.BitIncrement(TSelf)" /> public static double BitIncrement(double x) => Math.BitIncrement(x); /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.FusedMultiplyAdd(TSelf, TSelf, TSelf)" /> [Intrinsic] public static double FusedMultiplyAdd(double left, double right, double addend) => Math.FusedMultiplyAdd(left, right, addend); /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.Ieee754Remainder(TSelf, TSelf)" /> public static double Ieee754Remainder(double left, double right) => Math.IEEERemainder(left, right); /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.ILogB(TSelf)" /> public static int ILogB(double x) => Math.ILogB(x); /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.Lerp(TSelf, TSelf, TSelf)" /> public static double Lerp(double value1, double value2, double amount) => MultiplyAddEstimate(value1, 1.0 - amount, value2 * amount); /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.ReciprocalEstimate(TSelf)" /> [Intrinsic] public static double ReciprocalEstimate(double x) => Math.ReciprocalEstimate(x); /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.ReciprocalSqrtEstimate(TSelf)" /> [Intrinsic] public static double ReciprocalSqrtEstimate(double x) => Math.ReciprocalSqrtEstimate(x); /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.ScaleB(TSelf, int)" /> public static double ScaleB(double x, int n) => Math.ScaleB(x, n); // /// <inheritdoc cref="IFloatingPointIeee754{TSelf}.Compound(TSelf, TSelf)" /> // public static double Compound(double x, double n) => Math.Compound(x, n); // // IHyperbolicFunctions // /// <inheritdoc cref="IHyperbolicFunctions{TSelf}.Acosh(TSelf)" /> [Intrinsic] public static double Acosh(double x) => Math.Acosh(x); /// <inheritdoc cref="IHyperbolicFunctions{TSelf}.Asinh(TSelf)" /> [Intrinsic] public static double Asinh(double x) => Math.Asinh(x); /// <inheritdoc cref="IHyperbolicFunctions{TSelf}.Atanh(TSelf)" /> [Intrinsic] public static double Atanh(double x) => Math.Atanh(x); /// <inheritdoc cref="IHyperbolicFunctions{TSelf}.Cosh(TSelf)" /> [Intrinsic] public static double Cosh(double x) => Math.Cosh(x); /// <inheritdoc cref="IHyperbolicFunctions{TSelf}.Sinh(TSelf)" /> [Intrinsic] public static double Sinh(double x) => Math.Sinh(x); /// <inheritdoc cref="IHyperbolicFunctions{TSelf}.Tanh(TSelf)" /> [Intrinsic] public static double Tanh(double x) => Math.Tanh(x); // // IIncrementOperators // /// <inheritdoc cref="IIncrementOperators{TSelf}.op_Increment(TSelf)" /> static double IIncrementOperators<double>.operator ++(double value) => ++value; // // ILogarithmicFunctions // /// <inheritdoc cref="ILogarithmicFunctions{TSelf}.Log(TSelf)" /> [Intrinsic] public static double Log(double x) => Math.Log(x); /// <inheritdoc cref="ILogarithmicFunctions{TSelf}.Log(TSelf, TSelf)" /> public static double Log(double x, double newBase) => Math.Log(x, newBase); /// <inheritdoc cref="ILogarithmicFunctions{TSelf}.LogP1(TSelf)" /> public static double LogP1(double x) => Math.Log(x + 1); /// <inheritdoc cref="ILogarithmicFunctions{TSelf}.Log2P1(TSelf)" /> public static double Log2P1(double x) => Math.Log2(x + 1); /// <inheritdoc cref="ILogarithmicFunctions{TSelf}.Log10(TSelf)" /> [Intrinsic] public static double Log10(double x) => Math.Log10(x); /// <inheritdoc cref="ILogarithmicFunctions{TSelf}.Log10P1(TSelf)" /> public static double Log10P1(double x) => Math.Log10(x + 1); // // IMinMaxValue // /// <inheritdoc cref="IMinMaxValue{TSelf}.MinValue" /> static double IMinMaxValue<double>.MinValue => MinValue; /// <inheritdoc cref="IMinMaxValue{TSelf}.MaxValue" /> static double IMinMaxValue<double>.MaxValue => MaxValue; // // IModulusOperators // /// <inheritdoc cref="IModulusOperators{TSelf, TOther, TResult}.op_Modulus(TSelf, TOther)" /> static double IModulusOperators<double, double, double>.operator %(double left, double right) => left % right; // // IMultiplicativeIdentity // /// <inheritdoc cref="IMultiplicativeIdentity{TSelf, TResult}.MultiplicativeIdentity" /> static double IMultiplicativeIdentity<double, double>.MultiplicativeIdentity => MultiplicativeIdentity; // // IMultiplyOperators // /// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_Multiply(TSelf, TOther)" /> static double IMultiplyOperators<double, double, double>.operator *(double left, double right) => left * right; // // INumber // /// <inheritdoc cref="INumber{TSelf}.Clamp(TSelf, TSelf, TSelf)" /> public static double Clamp(double value, double min, double max) { if (min > max) { Math.ThrowMinMaxException(min, max); } return Min(Max(value, min), max); } /// <inheritdoc cref="INumber{TSelf}.ClampNative(TSelf, TSelf, TSelf)" /> public static double ClampNative(double value, double min, double max) { if (min > max) { Math.ThrowMinMaxException(min, max); } return MinNative(MaxNative(value, min), max); } /// <inheritdoc cref="INumber{TSelf}.CopySign(TSelf, TSelf)" /> public static double CopySign(double value, double sign) => Math.CopySign(value, sign); /// <inheritdoc cref="INumber{TSelf}.Max(TSelf, TSelf)" /> [Intrinsic] public static double Max(double x, double y) => Math.Max(x, y); /// <inheritdoc cref="INumber{TSelf}.MaxNative(TSelf, TSelf)" /> [Intrinsic] public static double MaxNative(double x, double y) => (x > y) ? x : y; /// <inheritdoc cref="INumber{TSelf}.MaxNumber(TSelf, TSelf)" /> [Intrinsic] public static double MaxNumber(double x, double y) { // This matches the IEEE 754:2019 `maximumNumber` function // // It does not propagate NaN inputs back to the caller and // otherwise returns the larger of the inputs. It // treats +0 as larger than -0 as per the specification. if (x != y) { if (!IsNaN(y)) { return y < x ? x : y; } return x; } return IsNegative(y) ? x : y; } /// <inheritdoc cref="INumber{TSelf}.Min(TSelf, TSelf)" /> [Intrinsic] public static double Min(double x, double y) => Math.Min(x, y); /// <inheritdoc cref="INumber{TSelf}.MinNative(TSelf, TSelf)" /> [Intrinsic] public static double MinNative(double x, double y) => (x < y) ? x : y; /// <inheritdoc cref="INumber{TSelf}.MinNumber(TSelf, TSelf)" /> [Intrinsic] public static double MinNumber(double x, double y) { // This matches the IEEE 754:2019 `minimumNumber` function // // It does not propagate NaN inputs back to the caller and // otherwise returns the larger of the inputs. It // treats +0 as larger than -0 as per the specification. if (x != y) { if (!IsNaN(y)) { return x < y ? x : y; } return x; } return IsNegative(x) ? x : y; } /// <inheritdoc cref="INumber{TSelf}.Sign(TSelf)" /> public static int Sign(double value) => Math.Sign(value); // // INumberBase // /// <inheritdoc cref="INumberBase{TSelf}.One" /> static double INumberBase<double>.One => One; /// <inheritdoc cref="INumberBase{TSelf}.Radix" /> static int INumberBase<double>.Radix => 2; /// <inheritdoc cref="INumberBase{TSelf}.Zero" /> static double INumberBase<double>.Zero => Zero; /// <inheritdoc cref="INumberBase{TSelf}.Abs(TSelf)" /> [Intrinsic] public static double Abs(double value) => Math.Abs(value); /// <inheritdoc cref="INumberBase{TSelf}.CreateChecked{TOther}(TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double CreateChecked<TOther>(TOther value) where TOther : INumberBase<TOther> { double result; if (typeof(TOther) == typeof(double)) { result = (double)(object)value; } else if (!TryConvertFrom(value, out result) && !TOther.TryConvertToChecked(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <inheritdoc cref="INumberBase{TSelf}.CreateSaturating{TOther}(TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double CreateSaturating<TOther>(TOther value) where TOther : INumberBase<TOther> { double result; if (typeof(TOther) == typeof(double)) { result = (double)(object)value; } else if (!TryConvertFrom(value, out result) && !TOther.TryConvertToSaturating(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <inheritdoc cref="INumberBase{TSelf}.CreateTruncating{TOther}(TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double CreateTruncating<TOther>(TOther value) where TOther : INumberBase<TOther> { double result; if (typeof(TOther) == typeof(double)) { result = (double)(object)value; } else if (!TryConvertFrom(value, out result) && !TOther.TryConvertToTruncating(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <inheritdoc cref="INumberBase{TSelf}.IsCanonical(TSelf)" /> static bool INumberBase<double>.IsCanonical(double value) => true; /// <inheritdoc cref="INumberBase{TSelf}.IsComplexNumber(TSelf)" /> static bool INumberBase<double>.IsComplexNumber(double value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsEvenInteger(TSelf)" /> public static bool IsEvenInteger(double value) => IsInteger(value) && (Abs(value % 2) == 0); /// <inheritdoc cref="INumberBase{TSelf}.IsImaginaryNumber(TSelf)" /> static bool INumberBase<double>.IsImaginaryNumber(double value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsInteger(TSelf)" /> public static bool IsInteger(double value) => IsFinite(value) && (value == Truncate(value)); /// <inheritdoc cref="INumberBase{TSelf}.IsOddInteger(TSelf)" /> public static bool IsOddInteger(double value) => IsInteger(value) && (Abs(value % 2) == 1); /// <inheritdoc cref="INumberBase{TSelf}.IsPositive(TSelf)" /> public static bool IsPositive(double value) => BitConverter.DoubleToInt64Bits(value) >= 0; /// <inheritdoc cref="INumberBase{TSelf}.IsRealNumber(TSelf)" /> public static bool IsRealNumber(double value) { // A NaN will never equal itself so this is an // easy and efficient way to check for a real number. #pragma warning disable CS1718 return value == value; #pragma warning restore CS1718 } /// <inheritdoc cref="INumberBase{TSelf}.IsZero(TSelf)" /> static bool INumberBase<double>.IsZero(double value) => IsZero(value); /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitude(TSelf, TSelf)" /> [Intrinsic] public static double MaxMagnitude(double x, double y) => Math.MaxMagnitude(x, y); /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitudeNumber(TSelf, TSelf)" /> [Intrinsic] public static double MaxMagnitudeNumber(double x, double y) { // This matches the IEEE 754:2019 `maximumMagnitudeNumber` function // // It does not propagate NaN inputs back to the caller and // otherwise returns the input with a larger magnitude. // It treats +0 as larger than -0 as per the specification. double ax = Abs(x); double ay = Abs(y); if ((ax > ay) || IsNaN(ay)) { return x; } if (ax == ay) { return IsNegative(x) ? y : x; } return y; } /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitude(TSelf, TSelf)" /> [Intrinsic] public static double MinMagnitude(double x, double y) => Math.MinMagnitude(x, y); /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitudeNumber(TSelf, TSelf)" /> [Intrinsic] public static double MinMagnitudeNumber(double x, double y) { // This matches the IEEE 754:2019 `minimumMagnitudeNumber` function // // It does not propagate NaN inputs back to the caller and // otherwise returns the input with a larger magnitude. // It treats +0 as larger than -0 as per the specification. double ax = Abs(x); double ay = Abs(y); if ((ax < ay) || IsNaN(ay)) { return x; } if (ax == ay) { return IsNegative(x) ? x : y; } return y; } /// <inheritdoc cref="INumberBase{TSelf}.MultiplyAddEstimate(TSelf, TSelf, TSelf)" /> [Intrinsic] public static double MultiplyAddEstimate(double left, double right, double addend) { #if MONO return (left * right) + addend; #else return MultiplyAddEstimate(left, right, addend); #endif } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromChecked{TOther}(TOther, out TSelf)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<double>.TryConvertFromChecked<TOther>(TOther value, out double result) { return TryConvertFrom(value, out result); } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromSaturating{TOther}(TOther, out TSelf)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<double>.TryConvertFromSaturating<TOther>(TOther value, out double result) { return TryConvertFrom(value, out result); } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromTruncating{TOther}(TOther, out TSelf)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<double>.TryConvertFromTruncating<TOther>(TOther value, out double result) { return TryConvertFrom(value, out result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryConvertFrom<TOther>(TOther value, out double result) where TOther : INumberBase<TOther> { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `double` will handle the other signed types and // `ConvertTo` will handle the unsigned types if (typeof(TOther) == typeof(Half)) { Half actualValue = (Half)(object)value; result = (double)actualValue; return true; } else if (typeof(TOther) == typeof(short)) { short actualValue = (short)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(int)) { int actualValue = (int)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(long)) { long actualValue = (long)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(Int128)) { Int128 actualValue = (Int128)(object)value; result = (double)actualValue; return true; } else if (typeof(TOther) == typeof(nint)) { nint actualValue = (nint)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(sbyte)) { sbyte actualValue = (sbyte)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(float)) { float actualValue = (float)(object)value; result = actualValue; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<double>.TryConvertToChecked<TOther>(double value, [MaybeNullWhen(false)] out TOther result) { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `double` will handle the other signed types and // `ConvertTo` will handle the unsigned types if (typeof(TOther) == typeof(byte)) { byte actualResult = checked((byte)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(char)) { char actualResult = checked((char)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(decimal)) { decimal actualResult = checked((decimal)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(ushort)) { ushort actualResult = checked((ushort)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(uint)) { uint actualResult = checked((uint)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(ulong)) { ulong actualResult = checked((ulong)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(UInt128)) { UInt128 actualResult = checked((UInt128)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(nuint)) { nuint actualResult = checked((nuint)value); result = (TOther)(object)actualResult; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<double>.TryConvertToSaturating<TOther>(double value, [MaybeNullWhen(false)] out TOther result) { return TryConvertTo(value, out result); } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<double>.TryConvertToTruncating<TOther>(double value, [MaybeNullWhen(false)] out TOther result) { return TryConvertTo(value, out result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryConvertTo<TOther>(double value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase<TOther> { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `double` will handle the other signed types and // `ConvertTo` will handle the unsigned types if (typeof(TOther) == typeof(byte)) { #if MONO byte actualResult = (value >= byte.MaxValue) ? byte.MaxValue : (value <= byte.MinValue) ? byte.MinValue : (byte)value; #else byte actualResult = (byte)value; #endif result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(char)) { #if MONO char actualResult = (value >= char.MaxValue) ? char.MaxValue : (value <= char.MinValue) ? char.MinValue : (char)value; #else char actualResult = (char)value; #endif result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(decimal)) { decimal actualResult = (value >= +79228162514264337593543950336.0) ? decimal.MaxValue : (value <= -79228162514264337593543950336.0) ? decimal.MinValue : IsNaN(value) ? 0.0m : (decimal)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(ushort)) { #if MONO ushort actualResult = (value >= ushort.MaxValue) ? ushort.MaxValue : (value <= ushort.MinValue) ? ushort.MinValue : (ushort)value; #else ushort actualResult = (ushort)value; #endif result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(uint)) { #if MONO uint actualResult = (value >= uint.MaxValue) ? uint.MaxValue : (value <= uint.MinValue) ? uint.MinValue : (uint)value; #else uint actualResult = (uint)value; #endif result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(ulong)) { #if MONO ulong actualResult = (value >= ulong.MaxValue) ? ulong.MaxValue : (value <= ulong.MinValue) ? ulong.MinValue : IsNaN(value) ? 0 : (ulong)value; #else ulong actualResult = (ulong)value; #endif result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(UInt128)) { UInt128 actualResult = (UInt128)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(nuint)) { #if MONO nuint actualResult = (value >= nuint.MaxValue) ? nuint.MaxValue : (value <= nuint.MinValue) ? nuint.MinValue : (nuint)value; #else nuint actualResult = (nuint)value; #endif result = (TOther)(object)actualResult; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(string, NumberStyles, IFormatProvider?, out TSelf, out int)" /> public static bool TryParsePartial([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out double result, out int charsConsumed) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); return Number.TryParseFloat(s.AsSpan(), style | Number.AllowTrailingInvalidCharacters, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed); } /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(ReadOnlySpan{char}, NumberStyles, IFormatProvider?, out TSelf, out int)" /> public static bool TryParsePartial(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out double result, out int charsConsumed) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); return Number.TryParseFloat(s, style | Number.AllowTrailingInvalidCharacters, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed); } /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?, out TSelf, out int)" /> public static bool TryParsePartial(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, out double result, out int bytesConsumed) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); return Number.TryParseFloat(utf8Text, style | Number.AllowTrailingInvalidCharacters, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed); } // // IParsable // /// <inheritdoc cref="IParsable{TSelf}.TryParse(string?, IFormatProvider?, out TSelf)" /> public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out double result) => TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider, out result); // // IPowerFunctions // /// <inheritdoc cref="IPowerFunctions{TSelf}.Pow(TSelf, TSelf)" /> [Intrinsic] public static double Pow(double x, double y) => Math.Pow(x, y); // // IRootFunctions // /// <inheritdoc cref="IRootFunctions{TSelf}.Cbrt(TSelf)" /> [Intrinsic] public static double Cbrt(double x) => Math.Cbrt(x); /// <inheritdoc cref="IRootFunctions{TSelf}.Hypot(TSelf, TSelf)" /> public static double Hypot(double x, double y) { // This code is based on `hypot` from amd/aocl-libm-ose // Copyright (C) 2008-2020 Advanced Micro Devices, Inc. All rights reserved. // // Licensed under the BSD 3-Clause "New" or "Revised" License // See THIRD-PARTY-NOTICES.TXT for the full license text double result; if (IsFinite(x) && IsFinite(y)) { double ax = Abs(x); double ay = Abs(y); if (ax == 0.0f) { result = ay; } else if (ay == 0.0f) { result = ax; } else { ulong xBits = BitConverter.DoubleToUInt64Bits(ax); ulong yBits = BitConverter.DoubleToUInt64Bits(ay); uint xExp = (uint)((xBits >> BiasedExponentShift) & ShiftedBiasedExponentMask); uint yExp = (uint)((yBits >> BiasedExponentShift) & ShiftedBiasedExponentMask); int expDiff = (int)(xExp - yExp); double expFix = 1.0; if ((expDiff <= (SignificandLength + 1)) && (expDiff >= (-SignificandLength - 1))) { if ((xExp > (ExponentBias + 500)) || (yExp > (ExponentBias + 500))) { // To prevent overflow, scale down by 2^+600 expFix = 4.149515568880993E+180; xBits -= 0x2580000000000000; yBits -= 0x2580000000000000; } else if ((xExp < (ExponentBias - 500)) || (yExp < (ExponentBias - 500))) { // To prevent underflow, scale up by 2^-600 expFix = 2.409919865102884E-181; xBits += 0x2580000000000000; yBits += 0x2580000000000000; // For subnormal values, do an additional fixing up changing the // adjustment to scale up by 2^601 instead and then subtract a // correction of 2^601 to account for the implicit bit. if (xExp == 0) // x is subnormal { xBits += 0x0010000000000000; ax = BitConverter.UInt64BitsToDouble(xBits); ax -= 9.232978617785736E-128; xBits = BitConverter.DoubleToUInt64Bits(ax); } if (yExp == 0) // y is subnormal { yBits += 0x0010000000000000; ay = BitConverter.UInt64BitsToDouble(yBits); ay -= 9.232978617785736E-128; yBits = BitConverter.DoubleToUInt64Bits(ay); } } ax = BitConverter.UInt64BitsToDouble(xBits); ay = BitConverter.UInt64BitsToDouble(yBits); if (ax < ay) { // Sort so ax is greater than ay double tmp = ax; ax = ay; ay = tmp; ulong tmpBits = xBits; xBits = yBits; yBits = tmpBits; } Debug.Assert(ax >= ay); // Split ax and ay into a head and tail portion double xHead = BitConverter.UInt64BitsToDouble(xBits & 0xFFFF_FFFF_F800_0000); double yHead = BitConverter.UInt64BitsToDouble(yBits & 0xFFFF_FFFF_F800_0000); double xTail = ax - xHead; double yTail = ay - yHead; // Compute (x * x) + (y * y) with extra precision // // This includes taking into account expFix which may // cause an underflow or overflow, but if it does that // will still be the correct result. double xx = ax * ax; double yy = ay * ay; double rHead = xx + yy; double rTail = (xx - rHead) + yy; rTail += (xHead * xHead) - xx; rTail += 2 * xHead * xTail; rTail += xTail * xTail; if (expDiff == 0) { // We only need to do extra accounting when ax and ay have equal exponents rTail += (yHead * yHead) - yy; rTail += 2 * yHead * yTail; rTail += yTail * yTail; } result = Sqrt(rHead + rTail) * expFix; } else { // x or y is insignificant compared to the other result = ax + ay; } } } else if (IsInfinity(x) || IsInfinity(y)) { // IEEE 754 requires that we return +Infinity // even if one of the inputs is NaN result = PositiveInfinity; } else { // IEEE 754 requires that we return NaN // if either input is NaN and neither is Infinity result = NaN; } return result; } /// <inheritdoc cref="IRootFunctions{TSelf}.RootN(TSelf, int)" /> public static double RootN(double x, int n) { double result; if (n > 0) { if (n == 2) { result = (x != 0.0) ? Sqrt(x) : 0.0; } else if (n == 3) { result = Cbrt(x); } else { result = PositiveN(x, n); } } else if (n < 0) { result = NegativeN(x, n); } else { Debug.Assert(n == 0); result = NaN; } return result; static double PositiveN(double x, int n) { double result; if (IsFinite(x)) { if (x != 0) { if ((x > 0) || IsOddInteger(n)) { result = Pow(Abs(x), 1.0 / n); result = CopySign(result, x); } else { result = NaN; } } else if (IsEvenInteger(n)) { result = 0.0; } else { result = CopySign(0.0, x); } } else if (IsNaN(x)) { result = NaN; } else if (x > 0) { Debug.Assert(IsPositiveInfinity(x)); result = PositiveInfinity; } else { Debug.Assert(IsNegativeInfinity(x)); result = int.IsOddInteger(n) ? NegativeInfinity : NaN; } return result; } static double NegativeN(double x, int n) { double result; if (IsFinite(x)) { if (x != 0) { if ((x > 0) || IsOddInteger(n)) { result = Pow(Abs(x), 1.0 / n); result = CopySign(result, x); } else { result = NaN; } } else if (IsEvenInteger(n)) { result = PositiveInfinity; } else { result = CopySign(PositiveInfinity, x); } } else if (IsNaN(x)) { result = NaN; } else if (x > 0) { Debug.Assert(IsPositiveInfinity(x)); result = 0.0; } else { Debug.Assert(IsNegativeInfinity(x)); result = int.IsOddInteger(n) ? -0.0 : NaN; } return result; } } /// <inheritdoc cref="IRootFunctions{TSelf}.Sqrt(TSelf)" /> [Intrinsic] public static double Sqrt(double x) => Math.Sqrt(x); // // ISignedNumber // /// <inheritdoc cref="ISignedNumber{TSelf}.NegativeOne" /> static double ISignedNumber<double>.NegativeOne => NegativeOne; // // ISpanParsable // /// <inheritdoc cref="ISpanParsable{TSelf}.Parse(ReadOnlySpan{char}, IFormatProvider?)" /> public static double Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider); /// <inheritdoc cref="ISpanParsable{TSelf}.TryParse(ReadOnlySpan{char}, IFormatProvider?, out TSelf)" /> public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out double result) => TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider, out result); // // ISubtractionOperators // /// <inheritdoc cref="ISubtractionOperators{TSelf, TOther, TResult}.op_Subtraction(TSelf, TOther)" /> static double ISubtractionOperators<double, double, double>.operator -(double left, double right) => left - right; // // ITrigonometricFunctions // /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.Acos(TSelf)" /> [Intrinsic] public static double Acos(double x) => Math.Acos(x); /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.AcosPi(TSelf)" /> public static double AcosPi(double x) { return Acos(x) / Pi; } /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.Asin(TSelf)" /> [Intrinsic] public static double Asin(double x) => Math.Asin(x); /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.AsinPi(TSelf)" /> public static double AsinPi(double x) { return Asin(x) / Pi; } /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.Atan(TSelf)" /> [Intrinsic] public static double Atan(double x) => Math.Atan(x); /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.AtanPi(TSelf)" /> public static double AtanPi(double x) { return Atan(x) / Pi; } /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.Cos(TSelf)" /> [Intrinsic] public static double Cos(double x) => Math.Cos(x); /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.CosPi(TSelf)" /> public static double CosPi(double x) { // This code is based on `cospi` from amd/aocl-libm-ose // Copyright (C) 2008-2020 Advanced Micro Devices, Inc. All rights reserved. // // Licensed under the BSD 3-Clause "New" or "Revised" License // See THIRD-PARTY-NOTICES.TXT for the full license text double result; if (IsFinite(x)) { double ax = Abs(x); if (ax < 4_503_599_627_370_496.0) // |x| < 2^52 { if (ax > 0.25) { long integral = (long)ax; double fractional = ax - integral; double sign = long.IsOddInteger(integral) ? -1.0 : +1.0; if (fractional <= 0.25) { if (fractional != 0.00) { result = sign * CosForIntervalPiBy4(fractional * Pi, 0.0); } else { result = sign; } } else if (fractional <= 0.50) { if (fractional != 0.50) { result = sign * SinForIntervalPiBy4((0.5 - fractional) * Pi, 0.0); } else { result = 0.0; } } else if (fractional <= 0.75) { result = -sign * SinForIntervalPiBy4((fractional - 0.5) * Pi, 0.0); } else { result = -sign * CosForIntervalPiBy4((1.0 - fractional) * Pi, 0.0); } } else if (ax >= 6.103515625E-05) // |x| >= 2^-14 { result = CosForIntervalPiBy4(x * Pi, 0.0); } else if (ax >= 7.450580596923828E-09) // |x| >= 2^-27 { double value = x * Pi; result = 1.0 - (value * value * 0.5); } else { result = 1.0; } } else if (ax < 9_007_199_254_740_992.0) // |x| < 2^53 { // x is an integer long bits = BitConverter.DoubleToInt64Bits(ax); result = long.IsOddInteger(bits) ? -1.0 : +1.0; } else { // x is an even integer result = 1.0; } } else { result = NaN; } return result; } /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.DegreesToRadians(TSelf)" /> public static double DegreesToRadians(double degrees) { // NOTE: Don't change the algorithm without consulting the DIM // which elaborates on why this implementation was chosen return (degrees * Pi) / 180.0; } /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.RadiansToDegrees(TSelf)" /> public static double RadiansToDegrees(double radians) { // NOTE: Don't change the algorithm without consulting the DIM // which elaborates on why this implementation was chosen return (radians * 180.0) / Pi; } /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.Sin(TSelf)" /> [Intrinsic] public static double Sin(double x) => Math.Sin(x); /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.SinCos(TSelf)" /> public static (double Sin, double Cos) SinCos(double x) => Math.SinCos(x); /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.SinCosPi(TSelf)" /> public static (double SinPi, double CosPi) SinCosPi(double x) { // This code is based on `cospi` and `sinpi` from amd/aocl-libm-ose // Copyright (C) 2008-2020 Advanced Micro Devices, Inc. All rights reserved. // // Licensed under the BSD 3-Clause "New" or "Revised" License // See THIRD-PARTY-NOTICES.TXT for the full license text double sinPi; double cosPi; if (IsFinite(x)) { double ax = Abs(x); if (ax < 4_503_599_627_370_496.0) // |x| < 2^52 { if (ax > 0.25) { long integral = (long)ax; double fractional = ax - integral; double sign = long.IsOddInteger(integral) ? -1.0 : +1.0; double sinSign = ((x > 0.0) ? +1.0 : -1.0) * sign; double cosSign = sign; if (fractional <= 0.25) { if (fractional != 0.00) { double value = fractional * Pi; sinPi = sinSign * SinForIntervalPiBy4(value, 0.0); cosPi = cosSign * CosForIntervalPiBy4(value, 0.0); } else { sinPi = x * 0.0; cosPi = cosSign; } } else if (fractional <= 0.50) { if (fractional != 0.50) { double value = (0.5 - fractional) * Pi; sinPi = sinSign * CosForIntervalPiBy4(value, 0.0); cosPi = cosSign * SinForIntervalPiBy4(value, 0.0); } else { sinPi = sinSign; cosPi = 0.0; } } else if (fractional <= 0.75) { double value = (fractional - 0.5) * Pi; sinPi = +sinSign * CosForIntervalPiBy4(value, 0.0); cosPi = -cosSign * SinForIntervalPiBy4(value, 0.0); } else { double value = (1.0 - fractional) * Pi; sinPi = +sinSign * SinForIntervalPiBy4(value, 0.0); cosPi = -cosSign * CosForIntervalPiBy4(value, 0.0); } } else if (ax >= 1.220703125E-4) // |x| >= 2^-13 { double value = x * Pi; sinPi = SinForIntervalPiBy4(value, 0.0); cosPi = CosForIntervalPiBy4(value, 0.0); } else if (ax >= 7.450580596923828E-09) // |x| >= 2^-27 { double value = x * Pi; double valueSq = value * value; sinPi = value - (valueSq * value * (1.0 / 6.0)); cosPi = 1.0 - (valueSq * 0.5); } else { sinPi = x * Pi; cosPi = 1.0; } } else if (ax < 9_007_199_254_740_992.0) // |x| < 2^53 { // x is an integer sinPi = x * 0.0; long bits = BitConverter.DoubleToInt64Bits(ax); cosPi = long.IsOddInteger(bits) ? -1.0 : +1.0; } else { // x is an even integer sinPi = x * 0.0; cosPi = 1.0; } } else { sinPi = NaN; cosPi = NaN; } return (sinPi, cosPi); } /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.SinPi(TSelf)" /> public static double SinPi(double x) { // This code is based on `sinpi` from amd/aocl-libm-ose // Copyright (C) 2008-2020 Advanced Micro Devices, Inc. All rights reserved. // // Licensed under the BSD 3-Clause "New" or "Revised" License // See THIRD-PARTY-NOTICES.TXT for the full license text double result; if (IsFinite(x)) { double ax = Abs(x); if (ax < 4_503_599_627_370_496.0) // |x| < 2^52 { if (ax > 0.25) { long integral = (long)ax; double fractional = ax - integral; double sign = ((x > 0.0) ? +1.0 : -1.0) * (long.IsOddInteger(integral) ? -1.0 : +1.0); if (fractional <= 0.25) { if (fractional != 0.00) { result = sign * SinForIntervalPiBy4(fractional * Pi, 0.0); } else { result = x * 0.0; } } else if (fractional <= 0.50) { if (fractional != 0.50) { result = sign * CosForIntervalPiBy4((0.5 - fractional) * Pi, 0.0); } else { result = sign; } } else if (fractional <= 0.75) { result = sign * CosForIntervalPiBy4((fractional - 0.5) * Pi, 0.0); } else { result = sign * SinForIntervalPiBy4((1.0 - fractional) * Pi, 0.0); } } else if (ax >= 1.220703125E-4) // |x| >= 2^-13 { result = SinForIntervalPiBy4(x * Pi, 0.0); } else if (ax >= 7.450580596923828E-09) // |x| >= 2^-27 { double value = x * Pi; result = value - (value * value * value * (1.0 / 6.0)); } else { result = x * Pi; } } else { // x is an integer result = x * 0.0; } } else { result = NaN; } return result; } /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.Tan(TSelf)" /> [Intrinsic] public static double Tan(double x) => Math.Tan(x); /// <inheritdoc cref="ITrigonometricFunctions{TSelf}.TanPi(TSelf)" /> public static double TanPi(double x) { // This code is based on `tanpi` from amd/aocl-libm-ose // Copyright (C) 2008-2020 Advanced Micro Devices, Inc. All rights reserved. // // Licensed under the BSD 3-Clause "New" or "Revised" License // See THIRD-PARTY-NOTICES.TXT for the full license text double result; if (IsFinite(x)) { double ax = Abs(x); double sign = (x > 0.0) ? +1.0 : -1.0; if (ax < 4_503_599_627_370_496.0) // |x| < 2^52 { if (ax > 0.25) { long integral = (long)ax; double fractional = ax - integral; if (fractional <= 0.25) { if (fractional != 0.00) { result = sign * TanForIntervalPiBy4(fractional * Pi, 0.0, isReciprocal: false); } else { result = sign * (long.IsOddInteger(integral) ? -0.0 : +0.0); } } else if (fractional <= 0.50) { if (fractional != 0.50) { result = -sign * TanForIntervalPiBy4((0.5 - fractional) * Pi, 0.0, isReciprocal: true); } else { result = +sign * (long.IsOddInteger(integral) ? NegativeInfinity : PositiveInfinity); } } else if (fractional <= 0.75) { result = +sign * TanForIntervalPiBy4((fractional - 0.5) * Pi, 0.0, isReciprocal: true); } else { result = -sign * TanForIntervalPiBy4((1.0 - fractional) * Pi, 0.0, isReciprocal: false); } } else if (ax >= 6.103515625E-05) // |x| >= 2^-14 { result = TanForIntervalPiBy4(x * Pi, 0.0, isReciprocal: false); } else if (ax >= 7.450580596923828E-09) // |x| >= 2^-27 { double value = x * Pi; result = value + (value * value * value * (1.0 / 3.0)); } else { result = x * Pi; } } else if (ax < 9_007_199_254_740_992.0) // |x| < 2^53 { // x is an integer long bits = BitConverter.DoubleToInt64Bits(ax); result = sign * (long.IsOddInteger(bits) ? -0.0 : +0.0); } else { // x is an even integer result = sign * 0.0; } } else { result = NaN; } return result; } // // IUnaryNegationOperators // /// <inheritdoc cref="IUnaryNegationOperators{TSelf, TResult}.op_UnaryNegation(TSelf)" /> static double IUnaryNegationOperators<double, double>.operator -(double value) => -value; // // IUnaryPlusOperators // /// <inheritdoc cref="IUnaryPlusOperators{TSelf, TResult}.op_UnaryPlus(TSelf)" /> static double IUnaryPlusOperators<double, double>.operator +(double value) => (double)(+value); // // IUtf8SpanParsable // /// <inheritdoc cref="INumberBase{TSelf}.Parse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?)" /> public static double Parse(ReadOnlySpan<byte> utf8Text, NumberStyles style = NumberStyles.Float | NumberStyles.AllowThousands, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); return Number.ParseFloat<byte, double>(utf8Text, style, NumberFormatInfo.GetInstance(provider)); } /// <inheritdoc cref="INumberBase{TSelf}.TryParse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?, out TSelf)" /> public static bool TryParse(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, out double result) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _); } /// <inheritdoc cref="IUtf8SpanParsable{TSelf}.Parse(ReadOnlySpan{byte}, IFormatProvider?)" /> public static double Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider) => Parse(utf8Text, NumberStyles.Float | NumberStyles.AllowThousands, provider); /// <inheritdoc cref="IUtf8SpanParsable{TSelf}.TryParse(ReadOnlySpan{byte}, IFormatProvider?, out TSelf)" /> public static bool TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, out double result) => TryParse(utf8Text, NumberStyles.Float | NumberStyles.AllowThousands, provider, out result); // // IBinaryFloatParseAndFormatInfo // static int IBinaryFloatParseAndFormatInfo<double>.NumberBufferLength => Number.DoubleNumberBufferLength; static ulong IBinaryFloatParseAndFormatInfo<double>.ZeroBits => 0; static ulong IBinaryFloatParseAndFormatInfo<double>.InfinityBits => 0x7FF00000_00000000; static ulong IBinaryFloatParseAndFormatInfo<double>.NormalMantissaMask => (1UL << SignificandLength) - 1; static ulong IBinaryFloatParseAndFormatInfo<double>.DenormalMantissaMask => TrailingSignificandMask; static int IBinaryFloatParseAndFormatInfo<double>.MinBinaryExponent => 1 - MaxExponent; static int IBinaryFloatParseAndFormatInfo<double>.MaxBinaryExponent => MaxExponent; static int IBinaryFloatParseAndFormatInfo<double>.MinDecimalExponent => -324; static int IBinaryFloatParseAndFormatInfo<double>.MaxDecimalExponent => 309; static int IBinaryFloatParseAndFormatInfo<double>.ExponentBias => ExponentBias; static ushort IBinaryFloatParseAndFormatInfo<double>.ExponentBits => 11; static int IBinaryFloatParseAndFormatInfo<double>.OverflowDecimalExponent => (MaxExponent + (2 * SignificandLength)) / 3; static int IBinaryFloatParseAndFormatInfo<double>.InfinityExponent => 0x7FF; static ushort IBinaryFloatParseAndFormatInfo<double>.NormalMantissaBits => SignificandLength; static ushort IBinaryFloatParseAndFormatInfo<double>.DenormalMantissaBits => TrailingSignificandLength; static int IBinaryFloatParseAndFormatInfo<double>.MinFastFloatDecimalExponent => -342; static int IBinaryFloatParseAndFormatInfo<double>.MaxFastFloatDecimalExponent => 308; static int IBinaryFloatParseAndFormatInfo<double>.MinExponentRoundToEven => -4; static int IBinaryFloatParseAndFormatInfo<double>.MaxExponentRoundToEven => 23; static int IBinaryFloatParseAndFormatInfo<double>.MaxExponentFastPath => 22; static ulong IBinaryFloatParseAndFormatInfo<double>.MaxMantissaFastPath => 2UL << TrailingSignificandLength; static double IBinaryFloatParseAndFormatInfo<double>.BitsToFloat(ulong bits) => BitConverter.UInt64BitsToDouble(bits); static ulong IBinaryFloatParseAndFormatInfo<double>.FloatToBits(double value) => BitConverter.DoubleToUInt64Bits(value); static int IBinaryFloatParseAndFormatInfo<double>.MaxRoundTripDigits => 17; static int IBinaryFloatParseAndFormatInfo<double>.MaxPrecisionCustomFormat => 15; // // Helpers // [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double CosForIntervalPiBy4(double x, double xTail) { // This code is based on `cos_piby4` from amd/aocl-libm-ose // Copyright (C) 2008-2020 Advanced Micro Devices, Inc. All rights reserved. // // Licensed under the BSD 3-Clause "New" or "Revised" License // See THIRD-PARTY-NOTICES.TXT for the full license text // Taylor series for cos(x) is: 1 - (x^2 / 2!) + (x^4 / 4!) - (x^6 / 6!) ... // // Then define f(xx) where xx = (x * x) // and f(xx) = 1 - (xx / 2!) + (xx^2 / 4!) - (xx^3 / 6!) ... // // We use a minimax approximation of (f(xx) - 1 + (xx / 2)) / (xx * xx) // because this produces an expansion in even powers of x. // // If xTail is non-zero, we subtract a correction term g(x, xTail) = (x * xTail) // to the result, where g(x, xTail) is an approximation to sin(x) * sin(xTail) // // This is valid because xTail is tiny relative to x. const double C1 = +0.41666666666666665390037E-1; // approx: +1 / 4! const double C2 = -0.13888888888887398280412E-2; // approx: -1 / 6! const double C3 = +0.248015872987670414957399E-4; // approx: +1 / 8! const double C4 = -0.275573172723441909470836E-6; // approx: -1 / 10! const double C5 = +0.208761463822329611076335E-8; // approx: +1 / 12! const double C6 = -0.113826398067944859590880E-10; // approx: -1 / 14! double xx = x * x; double tmp1 = 0.5 * xx; double tmp2 = 1.0 - tmp1; double result = C6; result = (result * xx) + C5; result = (result * xx) + C4; result = (result * xx) + C3; result = (result * xx) + C2; result = (result * xx) + C1; result *= (xx * xx); result += 1.0 - tmp2 - tmp1 - (x * xTail); result += tmp2; return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double SinForIntervalPiBy4(double x, double xTail) { // This code is based on `sin_piby4` from amd/aocl-libm-ose // Copyright (C) 2008-2020 Advanced Micro Devices, Inc. All rights reserved. // // Licensed under the BSD 3-Clause "New" or "Revised" License // See THIRD-PARTY-NOTICES.TXT for the full license text // Taylor series for sin(x) is x - (x^3 / 3!) + (x^5 / 5!) - (x^7 / 7!) ... // Which can be expressed as x * (1 - (x^2 / 3!) + (x^4 /5!) - (x^6 /7!) ...) // // Then define f(xx) where xx = (x * x) // and f(xx) = 1 - (xx / 3!) + (xx^2 / 5!) - (xx^3 / 7!) ... // // We use a minimax approximation of (f(xx) - 1) / xx // because this produces an expansion in even powers of x. // // If xTail is non-zero, we add a correction term g(x, xTail) = (1 - xx / 2) * xTail // to the result, where g(x, xTail) is an approximation to cos(x) * sin(xTail) // // This is valid because xTail is tiny relative to x. const double C1 = -0.166666666666666646259241729; // approx: -1 / 3! const double C2 = +0.833333333333095043065222816E-2; // approx: +1 / 5! const double C3 = -0.19841269836761125688538679E-3; // approx: -1 / 7! const double C4 = +0.275573161037288022676895908448E-5; // approx: +1 / 9! const double C5 = -0.25051132068021699772257377197E-7; // approx: -1 / 11! const double C6 = +0.159181443044859136852668200E-9; // approx: +1 / 13! double xx = x * x; double xxx = xx * x; double result = C6; result = (result * xx) + C5; result = (result * xx) + C4; result = (result * xx) + C3; result = (result * xx) + C2; if (xTail == 0.0) { result = (xx * result) + C1; result = (xxx * result) + x; } else { result = x - ((xx * ((0.5 * xTail) - (xxx * result))) - xTail - (xxx * C1)); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double TanForIntervalPiBy4(double x, double xTail, bool isReciprocal) { // This code is based on `tan_piby4` from amd/aocl-libm-ose // Copyright (C) 2008-2020 Advanced Micro Devices, Inc. All rights reserved. // // Licensed under the BSD 3-Clause "New" or "Revised" License // See THIRD-PARTY-NOTICES.TXT for the full license text // In order to maintain relative precision transform using the identity: // tan((pi / 4) - x) = (1 - tan(x)) / (1 + tan(x)) for arguments close to (pi / 4). // // Similarly use tan(x - (pi / 4)) = (tan(x) - 1) / (tan(x) + 1) close to (-pi / 4). const double PiBy4Head = 7.85398163397448278999E-01; const double PiBy4Tail = 3.06161699786838240164E-17; int transform = 0; if (x > +0.68) { transform = 1; x = (PiBy4Head - x) + (PiBy4Tail - xTail); xTail = 0.0; } else if (x < -0.68) { transform = -1; x = (PiBy4Head + x) + (PiBy4Tail + xTail); xTail = 0.0; } // Core Remez [2, 3] approximation to tan(x + xTail) on the interval [0, 0.68]. double tmp1 = (x * x) + (2.0 * x * xTail); double denominator = -0.232371494088563558304549252913E-3; denominator = +0.260656620398645407524064091208E-1 + (denominator * tmp1); denominator = -0.515658515729031149329237816945E+0 + (denominator * tmp1); denominator = +0.111713747927937668539901657944E+1 + (denominator * tmp1); double numerator = +0.224044448537022097264602535574E-3; numerator = -0.229345080057565662883358588111E-1 + (numerator * tmp1); numerator = +0.372379159759792203640806338901E+0 + (numerator * tmp1); double tmp2 = x * tmp1; tmp2 *= numerator / denominator; tmp2 += xTail; // Reconstruct tan(x) in the transformed case double result = x + tmp2; if (transform != 0) { if (isReciprocal) { result = (transform * (2 * result / (result - 1))) - 1.0; } else { result = transform * (1.0 - (2 * result / (1 + result))); } } else if (isReciprocal) { // Compute -1.0 / (x + tmp2) accurately ulong bits = BitConverter.DoubleToUInt64Bits(result); bits &= 0xFFFFFFFF00000000; double z1 = BitConverter.UInt64BitsToDouble(bits); double z2 = tmp2 - (z1 - x); double reciprocal = -1.0 / result; bits = BitConverter.DoubleToUInt64Bits(reciprocal); bits &= 0xFFFFFFFF00000000; double reciprocalHead = BitConverter.UInt64BitsToDouble(bits); result = reciprocalHead + (reciprocal * (1.0 + (reciprocalHead * z1) + (reciprocalHead * z2))); } return result; } } }