diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 3e91b3ecb8340..4a8f63df47f24 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4331,6 +4331,9 @@ Emitting debug info is not supported for this member. + + Object must be of type BFloat16. + The specified type must be a reference type, a primitive type, or an enum type. diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 2fdf95932689b..49bc04768b267 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -595,6 +595,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index dab5e73a27887..432926f6b1369 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -738,8 +738,8 @@ public static explicit operator Half(float value) const uint SingleBiasedExponentMask = float.BiasedExponentMask; // Exponent displacement #2 const uint Exponent13 = 0x0680_0000u; - // Maximum value that is not Infinity in Half - const float MaxHalfValueBelowInfinity = 65520.0f; + // The value above Half.MaxValue + const float HalfAboveMaxValue = 65520.0f; // Mask for exponent bits in Half const uint ExponentMask = BiasedExponentMask; uint bitValue = BitConverter.SingleToUInt32Bits(value); @@ -750,7 +750,7 @@ public static explicit operator Half(float value) // Clear sign bit value = float.Abs(value); // Rectify values that are Infinity in Half. (float.Min now emits vminps instruction if one of two arguments is a constant) - value = float.Min(MaxHalfValueBelowInfinity, value); + value = float.Min(HalfAboveMaxValue, value); // Rectify lower exponent uint exponentOffset0 = BitConverter.SingleToUInt32Bits(float.Max(value, BitConverter.UInt32BitsToSingle(MinExp))); // Extract exponent @@ -1362,7 +1362,7 @@ int IFloatingPoint.GetExponentShortestBitLength() int IFloatingPoint.GetSignificandByteCount() => sizeof(ushort); /// - int IFloatingPoint.GetSignificandBitLength() => 11; + int IFloatingPoint.GetSignificandBitLength() => SignificandLength; /// bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out int bytesWritten) @@ -2311,7 +2311,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo static int IBinaryFloatParseAndFormatInfo.NumberBufferLength => Number.HalfNumberBufferLength; static ulong IBinaryFloatParseAndFormatInfo.ZeroBits => 0; - static ulong IBinaryFloatParseAndFormatInfo.InfinityBits => 0x7C00; + static ulong IBinaryFloatParseAndFormatInfo.InfinityBits => PositiveInfinityBits; static ulong IBinaryFloatParseAndFormatInfo.NormalMantissaMask => (1UL << SignificandLength) - 1; static ulong IBinaryFloatParseAndFormatInfo.DenormalMantissaMask => TrailingSignificandMask; @@ -2323,15 +2323,15 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo static int IBinaryFloatParseAndFormatInfo.MaxDecimalExponent => 5; static int IBinaryFloatParseAndFormatInfo.ExponentBias => ExponentBias; - static ushort IBinaryFloatParseAndFormatInfo.ExponentBits => 5; + static ushort IBinaryFloatParseAndFormatInfo.ExponentBits => BiasedExponentLength; static int IBinaryFloatParseAndFormatInfo.OverflowDecimalExponent => (MaxExponent + (2 * SignificandLength)) / 3; - static int IBinaryFloatParseAndFormatInfo.InfinityExponent => 0x1F; + static int IBinaryFloatParseAndFormatInfo.InfinityExponent => MaxBiasedExponent; static ushort IBinaryFloatParseAndFormatInfo.NormalMantissaBits => SignificandLength; static ushort IBinaryFloatParseAndFormatInfo.DenormalMantissaBits => TrailingSignificandLength; - static int IBinaryFloatParseAndFormatInfo.MinFastFloatDecimalExponent => -8; + static int IBinaryFloatParseAndFormatInfo.MinFastFloatDecimalExponent => -26; static int IBinaryFloatParseAndFormatInfo.MaxFastFloatDecimalExponent => 4; static int IBinaryFloatParseAndFormatInfo.MinExponentRoundToEven => -21; diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index bb80ebd3ff67a..8faf707afcc09 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -50,6 +50,9 @@ internal interface IBinaryIntegerParseAndFormatInfo : IBinaryInteger : IBinaryFloatingPointIeee754, IMinMaxValue where TSelf : unmanaged, IBinaryFloatParseAndFormatInfo { + /// + /// Ceiling(Log10(5^(Abs(MinBinaryExponent) - 1))) + NormalMantissaBits + 1 + 1 + /// static abstract int NumberBufferLength { get; } static abstract ulong ZeroBits { get; } @@ -61,7 +64,14 @@ internal interface IBinaryFloatParseAndFormatInfo : IBinaryFloatingPointI static abstract int MinBinaryExponent { get; } static abstract int MaxBinaryExponent { get; } + /// + /// Floor(Log10(Epsilon)) + /// static abstract int MinDecimalExponent { get; } + + /// + /// Ceiling(Log10(MaxValue)) + /// static abstract int MaxDecimalExponent { get; } static abstract int ExponentBias { get; } @@ -73,12 +83,29 @@ internal interface IBinaryFloatParseAndFormatInfo : IBinaryFloatingPointI static abstract ushort NormalMantissaBits { get; } static abstract ushort DenormalMantissaBits { get; } + /// + /// Ceiling(Log10(2^(MinBinaryExponent - 1 - DenormalMantissaBits - 64))) + /// static abstract int MinFastFloatDecimalExponent { get; } + + /// + /// MaxDecimalExponent - 1 + /// static abstract int MaxFastFloatDecimalExponent { get; } + /// + /// -Floor(Log5(2^(64 - NormalMantissaBits))) + /// static abstract int MinExponentRoundToEven { get; } + + /// + /// Floor(Log5(2^(NormalMantissaBits + 1))) + /// static abstract int MaxExponentRoundToEven { get; } + /// + /// Max(n) when 10^n can be precisely represented + /// static abstract int MaxExponentFastPath { get; } static abstract ulong MaxMantissaFastPath { get; } @@ -86,16 +113,23 @@ internal interface IBinaryFloatParseAndFormatInfo : IBinaryFloatingPointI static abstract ulong FloatToBits(TSelf value); - // Maximum number of digits required to guarantee that any given floating point - // number can roundtrip. Some numbers may require less, but none will require more. + /// + /// Maximum number of digits required to guarantee that any given floating point + /// number can roundtrip. Some numbers may require less, but none will require more. + /// + /// + /// Ceiling(Log10(2^NormalMantissaBits)) + 1 + /// static abstract int MaxRoundTripDigits { get; } - // SinglePrecisionCustomFormat and DoublePrecisionCustomFormat are used to ensure that - // custom format strings return the same string as in previous releases when the format - // would return x digits or less (where x is the value of the corresponding constant). - // In order to support more digits, we would need to update ParseFormatSpecifier to pre-parse - // the format and determine exactly how many digits are being requested and whether they - // represent "significant digits" or "digits after the decimal point". + /// + /// MaxPrecisionCustomFormat is used to ensure that + /// custom format strings return the same string as in previous releases when the format + /// would return x digits or less (where x is the value of the corresponding constant). + /// In order to support more digits, we would need to update ParseFormatSpecifier to pre-parse + /// the format and determine exactly how many digits are being requested and whether they + /// represent "significant digits" or "digits after the decimal point". + /// static abstract int MaxPrecisionCustomFormat { get; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/BFloat16.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/BFloat16.cs new file mode 100644 index 0000000000000..04f5412fcbc52 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/BFloat16.cs @@ -0,0 +1,2011 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static System.Reflection.Emit.TypeNameBuilder; + +namespace System.Numerics +{ + /// + /// Represents a shortened (16-bit) version of 32 bit floating-point value (). + /// + public readonly struct BFloat16 + : IComparable, + ISpanFormattable, + IComparable, + IEquatable, + IBinaryFloatingPointIeee754, + IMinMaxValue, + IUtf8SpanFormattable, + IBinaryFloatParseAndFormatInfo + { + private const NumberStyles DefaultParseStyle = NumberStyles.Float | NumberStyles.AllowThousands; + + // Constants for manipulating the private bit-representation + + internal const ushort SignMask = 0x8000; + internal const int SignShift = 15; + internal const byte ShiftedSignMask = SignMask >> SignShift; + + internal const ushort BiasedExponentMask = 0x7F80; + internal const int BiasedExponentShift = 7; + internal const int BiasedExponentLength = 8; + internal const byte ShiftedBiasedExponentMask = BiasedExponentMask >> BiasedExponentShift; + + internal const ushort TrailingSignificandMask = 0x007F; + + internal const byte MinSign = 0; + internal const byte MaxSign = 1; + + internal const byte MinBiasedExponent = 0x00; + internal const byte MaxBiasedExponent = 0xFF; + + internal const byte ExponentBias = 127; + + internal const sbyte MinExponent = -126; + internal const sbyte MaxExponent = +127; + + internal const ushort MinTrailingSignificand = 0x0000; + internal const ushort MaxTrailingSignificand = 0x007F; + + internal const int TrailingSignificandLength = 7; + internal const int SignificandLength = TrailingSignificandLength + 1; + + // Constants representing the private bit-representation for various default values + + private const ushort PositiveZeroBits = 0x0000; + private const ushort NegativeZeroBits = 0x8000; + + private const ushort EpsilonBits = 0x0001; + + private const ushort PositiveInfinityBits = 0x7F80; + private const ushort NegativeInfinityBits = 0xFF80; + + // private const ushort PositiveQNaNBits = 0x7FC0; + private const ushort NegativeQNaNBits = 0xFFC0; + + private const ushort MinValueBits = 0xFF7F; + private const ushort MaxValueBits = 0x7F7F; + + private const ushort PositiveOneBits = 0x3F80; + private const ushort NegativeOneBits = 0xBF80; + + private const ushort SmallestNormalBits = 0x0080; + + private const ushort EBits = 0x402E; + private const ushort PiBits = 0x4049; + private const ushort TauBits = 0x40C9; + + // Well-defined and commonly used values + + public static BFloat16 Epsilon => new BFloat16(EpsilonBits); + + public static BFloat16 PositiveInfinity => new BFloat16(PositiveInfinityBits); + + public static BFloat16 NegativeInfinity => new BFloat16(NegativeInfinityBits); + + public static BFloat16 NaN => new BFloat16(NegativeQNaNBits); + + /// + public static BFloat16 MinValue => new BFloat16(MinValueBits); + + /// + public static BFloat16 MaxValue => new BFloat16(MaxValueBits); + + internal readonly ushort _value; + + internal BFloat16(ushort value) => _value = value; + + internal byte BiasedExponent + { + get + { + ushort bits = _value; + return ExtractBiasedExponentFromBits(bits); + } + } + + internal sbyte Exponent + { + get + { + return (sbyte)(BiasedExponent - ExponentBias); + } + } + + internal ushort Significand + { + get + { + return (ushort)(TrailingSignificand | ((BiasedExponent != 0) ? (1U << BiasedExponentShift) : 0U)); + } + } + + internal ushort TrailingSignificand + { + get + { + ushort bits = _value; + return ExtractTrailingSignificandFromBits(bits); + } + } + + internal static byte ExtractBiasedExponentFromBits(ushort bits) + { + return (byte)((bits >> BiasedExponentShift) & ShiftedBiasedExponentMask); + } + + internal static ushort ExtractTrailingSignificandFromBits(ushort bits) + { + return (ushort)(bits & TrailingSignificandMask); + } + + // INumberBase + + /// Determines whether the specified value is finite (zero, subnormal, or normal). + /// This effectively checks the value is not NaN and not infinite. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFinite(BFloat16 value) + { + uint bits = value._value; + return (~bits & PositiveInfinityBits) != 0; + } + + /// Determines whether the specified value is infinite. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInfinity(BFloat16 value) + { + uint bits = value._value; + return (bits & ~SignMask) == PositiveInfinityBits; + } + + /// Determines whether the specified value is NaN. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNaN(BFloat16 value) + { + uint bits = value._value; + return (bits & ~SignMask) > PositiveInfinityBits; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsNaNOrZero(BFloat16 value) + { + uint bits = value._value; + return ((bits - 1) & ~SignMask) >= PositiveInfinityBits; + } + + /// Determines whether the specified value is negative. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNegative(BFloat16 value) + { + return (short)(value._value) < 0; + } + + /// Determines whether the specified value is negative infinity. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNegativeInfinity(BFloat16 value) + { + return value._value == NegativeInfinityBits; + } + + /// Determines whether the specified value is normal (finite, but not zero or subnormal). + /// This effectively checks the value is not NaN, not infinite, not subnormal, and not zero. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNormal(BFloat16 value) + { + uint bits = value._value; + return (ushort)((bits & ~SignMask) - SmallestNormalBits) < (PositiveInfinityBits - SmallestNormalBits); + } + + /// Determines whether the specified value is positive infinity. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPositiveInfinity(BFloat16 value) + { + return value._value == PositiveInfinityBits; + } + + /// Determines whether the specified value is subnormal (finite, but not zero or normal). + /// This effectively checks the value is not NaN, not infinite, not normal, and not zero. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsSubnormal(BFloat16 value) + { + uint bits = value._value; + return (ushort)((bits & ~SignMask) - 1) < MaxTrailingSignificand; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsZero(BFloat16 value) + { + uint bits = value._value; + return (bits & ~SignMask) == 0; + } + + /// + /// Parses a from a in the default parse style. + /// + /// The input to be parsed. + /// The equivalent value representing the input string. If the input exceeds BFloat16's range, a or is returned. + public static BFloat16 Parse(string s) => Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider: null); + + /// + /// Parses a from a in the given . + /// + /// The input to be parsed. + /// The used to parse the input. + /// The equivalent value representing the input string. If the input exceeds BFloat16's range, a or is returned. + public static BFloat16 Parse(string s, NumberStyles style) => Parse(s, style, provider: null); + + /// + /// Parses a from a and . + /// + /// The input to be parsed. + /// A format provider. + /// The equivalent value representing the input string. If the input exceeds BFloat16's range, a or is returned. + public static BFloat16 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider); + + /// + /// Parses a from a with the given and . + /// + /// The input to be parsed. + /// The used to parse the input. + /// A format provider. + /// The equivalent value representing the input string. If the input exceeds BFloat16's range, a or is returned. + public static BFloat16 Parse(string s, NumberStyles style = DefaultParseStyle, IFormatProvider? provider = null) + { + if (s is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + } + return Parse(s.AsSpan(), style, provider); + } + + /// + /// Parses a from a and . + /// + /// The input to be parsed. + /// The used to parse the input. + /// A format provider. + /// The equivalent value representing the input string. If the input exceeds BFloat16's range, a or is returned. + public static BFloat16 Parse(ReadOnlySpan s, NumberStyles style = DefaultParseStyle, IFormatProvider? provider = null) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.ParseFloat(s, style, NumberFormatInfo.GetInstance(provider)); + } + + /// + /// Tries to parse a from a in the default parse style. + /// + /// The input to be parsed. + /// The equivalent value representing the input string if the parse was successful. If the input exceeds BFloat16's range, a or is returned. If the parse was unsuccessful, a default value is returned. + /// if the parse was successful, otherwise. + public static bool TryParse([NotNullWhen(true)] string? s, out BFloat16 result) => TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider: null, out result); + + /// + /// Tries to parse a from a in the default parse style. + /// + /// The input to be parsed. + /// The equivalent value representing the input string if the parse was successful. If the input exceeds BFloat16's range, a or is returned. If the parse was unsuccessful, a default value is returned. + /// if the parse was successful, otherwise. + public static bool TryParse(ReadOnlySpan s, out BFloat16 result) => TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider: null, out result); + + /// Tries to convert a UTF-8 character span containing the string representation of a number to its number equivalent. + /// A read-only UTF-8 character span that contains the number to convert. + /// When this method returns, contains a number equivalent of the numeric value or symbol contained in if the conversion succeeded or zero if the conversion failed. The conversion fails if the is or is not in a valid format. This parameter is passed uninitialized; any value originally supplied in result will be overwritten. + /// true if was converted successfully; otherwise, false. + public static bool TryParse(ReadOnlySpan utf8Text, out BFloat16 result) => TryParse(utf8Text, NumberStyles.Float | NumberStyles.AllowThousands, provider: null, out result); + + /// + /// Tries to parse a from a with the given and . + /// + /// The input to be parsed. + /// The used to parse the input. + /// A format provider. + /// The equivalent value representing the input string if the parse was successful. If the input exceeds BFloat16's range, a or is returned. If the parse was unsuccessful, a default value is returned. + /// if the parse was successful, otherwise. + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out BFloat16 result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s == null) + { + result = Zero; + return false; + } + return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result); + } + + /// + /// Tries to parse a from a with the given and . + /// + /// The input to be parsed. + /// The used to parse the input. + /// A format provider. + /// The equivalent value representing the input string if the parse was successful. If the input exceeds BFloat16's range, a or is returned. If the parse was unsuccessful, a default value is returned. + /// if the parse was successful, otherwise. + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out BFloat16 result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + // Comparison + + /// + /// Compares this object to another object, returning an integer that indicates the relationship. + /// + /// A value less than zero if this is less than , zero if this is equal to , or a value greater than zero if this is greater than . + /// Thrown when is not of type . + public int CompareTo(object? obj) + { + if (obj is not BFloat16 other) + { + return (obj is null) ? 1 : throw new ArgumentException(SR.Arg_MustBeBFloat16); + } + return CompareTo(other); + } + + /// + /// Compares this object to another object, returning an integer that indicates the relationship. + /// + /// A value less than zero if this is less than , zero if this is equal to , or a value greater than zero if this is greater than . + public int CompareTo(BFloat16 other) => ((float)this).CompareTo((float)other); + + /// + public static bool operator ==(BFloat16 left, BFloat16 right) => (float)left == (float)right; + + /// + public static bool operator !=(BFloat16 left, BFloat16 right) => (float)left != (float)right; + + /// + public static bool operator <(BFloat16 left, BFloat16 right) => (float)left < (float)right; + + /// + public static bool operator >(BFloat16 left, BFloat16 right) => (float)left > (float)right; + + /// + public static bool operator <=(BFloat16 left, BFloat16 right) => (float)left <= (float)right; + + /// + public static bool operator >=(BFloat16 left, BFloat16 right) => (float)left >= (float)right; + + // Equality + + /// + /// Returns a value that indicates whether this instance is equal to a specified value. + /// + public bool Equals(BFloat16 other) => ((float)this).Equals((float)other); + + /// + /// Returns a value that indicates whether this instance is equal to a specified . + /// + public override bool Equals(object? obj) => obj is BFloat16 other && Equals(other); + + /// + /// Serves as the default hash function. + /// + public override int GetHashCode() => ((float)this).GetHashCode(); + + /// + /// Returns a string representation of the current value. + /// + public override string ToString() => Number.FormatFloat(this, null, NumberFormatInfo.CurrentInfo); + + /// + /// Returns a string representation of the current value using the specified . + /// + public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) + { + return Number.FormatFloat(this, format, NumberFormatInfo.CurrentInfo); + } + + /// + /// Returns a string representation of the current value with the specified . + /// + public string ToString(IFormatProvider? provider) + { + return Number.FormatFloat(this, null, NumberFormatInfo.GetInstance(provider)); + } + + /// + /// Returns a string representation of the current value using the specified and . + /// + public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider) + { + return Number.FormatFloat(this, format, NumberFormatInfo.GetInstance(provider)); + } + + /// + /// Tries to format the value of the current BFloat16 instance into the provided span of characters. + /// + /// When this method returns, this instance's value formatted as a span of characters. + /// When this method returns, the number of characters that were written in . + /// A span containing the characters that represent a standard or custom format string that defines the acceptable format for . + /// An optional object that supplies culture-specific formatting information for . + /// + public bool TryFormat(Span destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan format = default, IFormatProvider? provider = null) + { + return Number.TryFormatFloat(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + } + + /// + public bool TryFormat(Span utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan format = default, IFormatProvider? provider = null) + { + return Number.TryFormatFloat(this, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); + } + + // + // Explicit Convert To BFloat16 + // + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator BFloat16(char value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator BFloat16(decimal value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator BFloat16(double value) + { + // See explaination of the algorithm at Half.operator Half(float) + + // Minimum exponent for rounding + const ulong MinExp = 0x3810_0000_0000_0000u; + // Exponent displacement #1 + const ulong Exponent942 = 0x3ae0_0000_0000_0000u; + // Exponent mask + const ulong SingleBiasedExponentMask = double.BiasedExponentMask; + // Exponent displacement #2 + const ulong Exponent45 = 0x02D0_0000_0000_0000u; + // The value above BFloat16.MaxValue + const double BFloat16AboveMaxValue = 3.39617752923046E+38; + // Mask for exponent bits in BFloat16 + const ulong ExponentMask = BiasedExponentMask; + ulong bitValue = BitConverter.DoubleToUInt64Bits(value); + // Extract sign bit + ulong sign = (bitValue & double.SignMask) >> 48; + // Detecting NaN (~0u if a is not NaN) + ulong realMask = (ulong)(Unsafe.BitCast(double.IsNaN(value)) - 1); + // Clear sign bit + value = double.Abs(value); + // Rectify values that are Infinity in BFloat16. (float.Min now emits vminps instruction if one of two arguments is a constant) + value = double.Min(BFloat16AboveMaxValue, value); + // Rectify lower exponent + ulong exponentOffset0 = BitConverter.DoubleToUInt64Bits(double.Max(value, BitConverter.UInt64BitsToDouble(MinExp))); + // Extract exponent + exponentOffset0 &= SingleBiasedExponentMask; + // Add exponent by 45 + exponentOffset0 += Exponent45; + // Round Single into BFloat16's precision (NaN also gets modified here, just setting the MSB of fraction) + value += BitConverter.UInt64BitsToDouble(exponentOffset0); + bitValue = BitConverter.DoubleToUInt64Bits(value); + // Only exponent bits will be modified if NaN + ulong maskedBFloat16ExponentForNaN = ~realMask & ExponentMask; + // Subtract exponent by 942 + bitValue -= Exponent942; + // Shift bitValue right by 45 bits to match the boundary of exponent part and fraction part. + ulong newExponent = bitValue >> 45; + // Clear the fraction parts if the value was NaN. + bitValue &= realMask; + // Merge the exponent part with fraction part, and add the exponent part and fraction part's overflow. + bitValue += newExponent; + // Clear exponents if value is NaN + bitValue &= ~maskedBFloat16ExponentForNaN; + // Merge sign bit with possible NaN exponent + ulong signAndMaskedExponent = maskedBFloat16ExponentForNaN | sign; + // Merge sign bit and possible NaN exponent + bitValue |= signAndMaskedExponent; + // The final result + return new BFloat16((ushort)bitValue); + } + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator BFloat16(short value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator BFloat16(Half value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator BFloat16(int value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator BFloat16(long value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator BFloat16(Int128 value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator BFloat16(nint value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator BFloat16(float value) + { + uint bits = BitConverter.SingleToUInt32Bits(value); + uint upper = bits >> 16; + // Only do rounding for finite numbers + if (float.IsFinite(value)) + { + uint lower = (ushort)bits; + // Determine the increment for rounding + // When upper is even, midpoint (0x8000) will tie to no increment, which is effectively a decrement of lower + uint lowerShift = (~upper) & (lower >> 15) & 1; // Upper is even & lower>=0x8000 (not 0) + lower -= lowerShift; + uint increment = lower >> 15; + // Do the increment, MaxValue will be correctly increased to Infinity + upper += increment; + } + return new BFloat16((ushort)upper); + } + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + [CLSCompliant(false)] + public static explicit operator BFloat16(ushort value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + [CLSCompliant(false)] + public static explicit operator BFloat16(uint value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + [CLSCompliant(false)] + public static explicit operator BFloat16(ulong value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + [CLSCompliant(false)] + public static explicit operator BFloat16(nuint value) => (BFloat16)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + [CLSCompliant(false)] + public static explicit operator BFloat16(UInt128 value) => (BFloat16)(float)value; + + // + // Explicit Convert From BFloat16 + // + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator byte(BFloat16 value) => (byte)(float)value; + + /// Explicitly converts a value to its nearest representable value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to its nearest representable value. + /// is not representable by . + public static explicit operator checked byte(BFloat16 value) => checked((byte)(float)value); + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator char(BFloat16 value) => (char)(float)value; + + /// Explicitly converts a value to its nearest representable value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to its nearest representable value. + /// is not representable by . + public static explicit operator checked char(BFloat16 value) => checked((char)(float)value); + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator decimal(BFloat16 value) => (decimal)(float)value; + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator short(BFloat16 value) => (short)(float)value; + + /// Explicitly converts a value to its nearest representable value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to its nearest representable value. + /// is not representable by . + public static explicit operator checked short(BFloat16 value) => checked((short)(float)value); + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator int(BFloat16 value) => (int)(float)value; + + /// Explicitly converts a value to its nearest representable value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to its nearest representable value. + /// is not representable by . + public static explicit operator checked int(BFloat16 value) => checked((int)(float)value); + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator long(BFloat16 value) => (long)(float)value; + + /// Explicitly converts a value to its nearest representable value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to its nearest representable value. + /// is not representable by . + public static explicit operator checked long(BFloat16 value) => checked((long)(float)value); + + /// Explicitly converts a value to its nearest representable . + /// The value to convert. + /// converted to a 128-bit signed integer. + public static explicit operator Int128(BFloat16 value) => (Int128)(double)(value); + + /// Explicitly converts a value to its nearest representable , throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit signed integer. + /// is not representable by . + public static explicit operator checked Int128(BFloat16 value) => checked((Int128)(double)(value)); + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator nint(BFloat16 value) => (nint)(float)value; + + /// Explicitly converts a value to its nearest representable value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to its nearest representable value. + /// is not representable by . + public static explicit operator checked nint(BFloat16 value) => checked((nint)(float)value); + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + [CLSCompliant(false)] + public static explicit operator sbyte(BFloat16 value) => (sbyte)(float)value; + + /// Explicitly converts a value to its nearest representable value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to its nearest representable value. + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked sbyte(BFloat16 value) => checked((sbyte)(float)value); + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + [CLSCompliant(false)] + public static explicit operator ushort(BFloat16 value) => (ushort)(float)value; + + /// Explicitly converts a value to its nearest representable value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to its nearest representable value. + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked ushort(BFloat16 value) => checked((ushort)(float)value); + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + [CLSCompliant(false)] + public static explicit operator uint(BFloat16 value) => (uint)(float)value; + + /// Explicitly converts a value to its nearest representable value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to its nearest representable value. + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked uint(BFloat16 value) => checked((uint)(float)value); + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + [CLSCompliant(false)] + public static explicit operator ulong(BFloat16 value) => (ulong)(float)value; + + /// Explicitly converts a value to its nearest representable value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to its nearest representable value. + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked ulong(BFloat16 value) => checked((ulong)(float)value); + + /// Explicitly converts a value to its nearest representable . + /// The value to convert. + /// converted to a 128-bit unsigned integer. + [CLSCompliant(false)] + public static explicit operator UInt128(BFloat16 value) => (UInt128)(double)(value); + + /// Explicitly converts a value to its nearest representable , throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked UInt128(BFloat16 value) => checked((UInt128)(double)(value)); + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + [CLSCompliant(false)] + public static explicit operator nuint(BFloat16 value) => (nuint)(float)value; + + /// Explicitly converts a value to its nearest representable value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to its nearest representable value. + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked nuint(BFloat16 value) => checked((nuint)(float)value); + + // + // Implicit Convert To BFloat16 + // + + /// Implicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static implicit operator BFloat16(byte value) => (BFloat16)(float)value; + + /// Implicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + [CLSCompliant(false)] + public static implicit operator BFloat16(sbyte value) => (BFloat16)(float)value; + + // + // Implicit Convert From Half (actually explicit) + // + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + + public static explicit operator float(BFloat16 value) => BitConverter.Int32BitsToSingle(value._value << 16); + + /// Explicitly converts a value to its nearest representable value. + /// The value to convert. + /// converted to its nearest representable value. + public static explicit operator double(BFloat16 value) => (double)(float)value; + + // + // IAdditionOperators + // + + /// + public static BFloat16 operator +(BFloat16 left, BFloat16 right) => (BFloat16)((float)left + (float)right); + + // + // IAdditiveIdentity + // + + /// + static BFloat16 IAdditiveIdentity.AdditiveIdentity => new BFloat16(PositiveZeroBits); + + // + // IBinaryNumber + // + + /// + static BFloat16 IBinaryNumber.AllBitsSet => new BFloat16(0xFFFF); + + /// + public static bool IsPow2(BFloat16 value) + { + ushort bits = value._value; + + if ((short)bits <= 0) + { + // Zero and negative values cannot be powers of 2 + return false; + } + + byte biasedExponent = ExtractBiasedExponentFromBits(bits); + ushort trailingSignificand = ExtractTrailingSignificandFromBits(bits); + + if (biasedExponent == MinBiasedExponent) + { + // Subnormal values have 1 bit set when they're powers of 2 + return ushort.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; + } + + /// + public static BFloat16 Log2(BFloat16 value) => (BFloat16)MathF.Log2((float)value); + + // + // IBitwiseOperators + // + + /// + static BFloat16 IBitwiseOperators.operator &(BFloat16 left, BFloat16 right) + { + return new BFloat16((ushort)(left._value & right._value)); + } + + /// + static BFloat16 IBitwiseOperators.operator |(BFloat16 left, BFloat16 right) + { + return new BFloat16((ushort)(left._value | right._value)); + } + + /// + static BFloat16 IBitwiseOperators.operator ^(BFloat16 left, BFloat16 right) + { + return new BFloat16((ushort)(left._value ^ right._value)); + } + + /// + static BFloat16 IBitwiseOperators.operator ~(BFloat16 value) + { + return new BFloat16((ushort)(~value._value)); + } + + // + // IDecrementOperators + // + + /// + public static BFloat16 operator --(BFloat16 value) + { + var tmp = (float)value; + --tmp; + return (BFloat16)tmp; + } + + // + // IDivisionOperators + // + + /// + public static BFloat16 operator /(BFloat16 left, BFloat16 right) => (BFloat16)((float)left / (float)right); + + // + // IExponentialFunctions + // + + /// + public static BFloat16 Exp(BFloat16 x) => (BFloat16)MathF.Exp((float)x); + + /// + public static BFloat16 ExpM1(BFloat16 x) => (BFloat16)float.ExpM1((float)x); + + /// + public static BFloat16 Exp2(BFloat16 x) => (BFloat16)float.Exp2((float)x); + + /// + public static BFloat16 Exp2M1(BFloat16 x) => (BFloat16)float.Exp2M1((float)x); + + /// + public static BFloat16 Exp10(BFloat16 x) => (BFloat16)float.Exp10((float)x); + + /// + public static BFloat16 Exp10M1(BFloat16 x) => (BFloat16)float.Exp10M1((float)x); + + // + // IFloatingPoint + // + + /// + public static BFloat16 Ceiling(BFloat16 x) => (BFloat16)MathF.Ceiling((float)x); + + /// + public static BFloat16 Floor(BFloat16 x) => (BFloat16)MathF.Floor((float)x); + + /// + public static BFloat16 Round(BFloat16 x) => (BFloat16)MathF.Round((float)x); + + /// + public static BFloat16 Round(BFloat16 x, int digits) => (BFloat16)MathF.Round((float)x, digits); + + /// + public static BFloat16 Round(BFloat16 x, MidpointRounding mode) => (BFloat16)MathF.Round((float)x, mode); + + /// + public static BFloat16 Round(BFloat16 x, int digits, MidpointRounding mode) => (BFloat16)MathF.Round((float)x, digits, mode); + + /// + public static BFloat16 Truncate(BFloat16 x) => (BFloat16)MathF.Truncate((float)x); + + /// + int IFloatingPoint.GetExponentByteCount() => sizeof(sbyte); + + /// + int IFloatingPoint.GetExponentShortestBitLength() + { + sbyte exponent = Exponent; + + if (exponent >= 0) + { + return (sizeof(sbyte) * 8) - sbyte.LeadingZeroCount(exponent); + } + else + { + return (sizeof(sbyte) * 8) + 1 - sbyte.LeadingZeroCount((sbyte)(~exponent)); + } + } + + /// + int IFloatingPoint.GetSignificandByteCount() => sizeof(ushort); + + /// + int IFloatingPoint.GetSignificandBitLength() => SignificandLength; + + /// + bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out int bytesWritten) + { + if (destination.Length >= sizeof(sbyte)) + { + destination[0] = (byte)Exponent; + bytesWritten = sizeof(sbyte); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, out int bytesWritten) + { + if (destination.Length >= sizeof(sbyte)) + { + destination[0] = (byte)Exponent; + bytesWritten = sizeof(sbyte); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteUInt16BigEndian(destination, Significand)) + { + bytesWritten = sizeof(uint); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteUInt16LittleEndian(destination, Significand)) + { + bytesWritten = sizeof(uint); + return true; + } + + bytesWritten = 0; + return false; + } + + // + // IFloatingPointConstants + // + + /// + public static BFloat16 E => new BFloat16(EBits); + + /// + public static BFloat16 Pi => new BFloat16(PiBits); + + /// + public static BFloat16 Tau => new BFloat16(TauBits); + + // + // IFloatingPointIeee754 + // + + /// + public static BFloat16 NegativeZero => new BFloat16(NegativeZeroBits); + + /// + public static BFloat16 Atan2(BFloat16 y, BFloat16 x) => (BFloat16)MathF.Atan2((float)y, (float)x); + + /// + public static BFloat16 Atan2Pi(BFloat16 y, BFloat16 x) => (BFloat16)float.Atan2Pi((float)y, (float)x); + + /// + public static BFloat16 BitDecrement(BFloat16 x) + { + uint bits = x._value; + + if (!IsFinite(x)) + { + // NaN returns NaN + // -Infinity returns -Infinity + // +Infinity returns MaxValue + return (bits == PositiveInfinityBits) ? MaxValue : x; + } + + if (bits == PositiveZeroBits) + { + // +0.0 returns -Epsilon + return -Epsilon; + } + + // Negative values need to be incremented + // Positive values need to be decremented + + if (IsNegative(x)) + { + bits += 1; + } + else + { + bits -= 1; + } + return new BFloat16((ushort)bits); + } + + /// + public static BFloat16 BitIncrement(BFloat16 x) + { + uint bits = x._value; + + if (!IsFinite(x)) + { + // NaN returns NaN + // -Infinity returns MinValue + // +Infinity returns +Infinity + return (bits == NegativeInfinityBits) ? MinValue : x; + } + + if (bits == NegativeZeroBits) + { + // -0.0 returns Epsilon + return Epsilon; + } + + // Negative values need to be decremented + // Positive values need to be incremented + + if (IsNegative(x)) + { + bits -= 1; + } + else + { + bits += 1; + } + return new BFloat16((ushort)bits); + } + + /// + public static BFloat16 FusedMultiplyAdd(BFloat16 left, BFloat16 right, BFloat16 addend) => (BFloat16)MathF.FusedMultiplyAdd((float)left, (float)right, (float)addend); + + /// + public static BFloat16 Ieee754Remainder(BFloat16 left, BFloat16 right) => (BFloat16)MathF.IEEERemainder((float)left, (float)right); + + /// + public static int ILogB(BFloat16 x) + { + // This code is based on `ilogbf` from amd/aocl-libm-ose + // Copyright (C) 2008-2022 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 + + if (!IsNormal(x)) // x is zero, subnormal, infinity, or NaN + { + if (IsZero(x)) + { + return int.MinValue; + } + + if (!IsFinite(x)) // infinity or NaN + { + return int.MaxValue; + } + + Debug.Assert(IsSubnormal(x)); + return MinExponent - (BitOperations.TrailingZeroCount(x.TrailingSignificand) - BiasedExponentLength); + } + + return x.Exponent; + } + + /// + public static BFloat16 Lerp(BFloat16 value1, BFloat16 value2, BFloat16 amount) => (BFloat16)float.Lerp((float)value1, (float)value2, (float)amount); + + /// + public static BFloat16 ReciprocalEstimate(BFloat16 x) => (BFloat16)MathF.ReciprocalEstimate((float)x); + + /// + public static BFloat16 ReciprocalSqrtEstimate(BFloat16 x) => (BFloat16)MathF.ReciprocalSqrtEstimate((float)x); + + /// + public static BFloat16 ScaleB(BFloat16 x, int n) => (BFloat16)MathF.ScaleB((float)x, n); + + // /// + // public static BFloat16 Compound(BFloat16 x, BFloat16 n) => (BFloat16)MathF.Compound((float)x, (float)n); + + // + // IHyperbolicFunctions + // + + /// + public static BFloat16 Acosh(BFloat16 x) => (BFloat16)MathF.Acosh((float)x); + + /// + public static BFloat16 Asinh(BFloat16 x) => (BFloat16)MathF.Asinh((float)x); + + /// + public static BFloat16 Atanh(BFloat16 x) => (BFloat16)MathF.Atanh((float)x); + + /// + public static BFloat16 Cosh(BFloat16 x) => (BFloat16)MathF.Cosh((float)x); + + /// + public static BFloat16 Sinh(BFloat16 x) => (BFloat16)MathF.Sinh((float)x); + + /// + public static BFloat16 Tanh(BFloat16 x) => (BFloat16)MathF.Tanh((float)x); + + // + // IIncrementOperators + // + + /// + public static BFloat16 operator ++(BFloat16 value) + { + var tmp = (float)value; + ++tmp; + return (BFloat16)tmp; + } + + // + // ILogarithmicFunctions + // + + /// + public static BFloat16 Log(BFloat16 x) => (BFloat16)MathF.Log((float)x); + + /// + public static BFloat16 Log(BFloat16 x, BFloat16 newBase) => (BFloat16)MathF.Log((float)x, (float)newBase); + + /// + public static BFloat16 Log10(BFloat16 x) => (BFloat16)MathF.Log10((float)x); + + /// + public static BFloat16 LogP1(BFloat16 x) => (BFloat16)float.LogP1((float)x); + + /// + public static BFloat16 Log2P1(BFloat16 x) => (BFloat16)float.Log2P1((float)x); + + /// + public static BFloat16 Log10P1(BFloat16 x) => (BFloat16)float.Log10P1((float)x); + + // + // IModulusOperators + // + + /// + public static BFloat16 operator %(BFloat16 left, BFloat16 right) => (BFloat16)((float)left % (float)right); + + // + // IMultiplicativeIdentity + // + + /// + public static BFloat16 MultiplicativeIdentity => new BFloat16(PositiveOneBits); + + // + // IMultiplyOperators + // + + /// + public static BFloat16 operator *(BFloat16 left, BFloat16 right) => (BFloat16)((float)left * (float)right); + + // + // INumber + // + + /// + public static BFloat16 Clamp(BFloat16 value, BFloat16 min, BFloat16 max) => (BFloat16)Math.Clamp((float)value, (float)min, (float)max); + + /// + public static BFloat16 CopySign(BFloat16 value, BFloat16 sign) + { + // This method is required to work for all inputs, + // including NaN, so we operate on the raw bits. + uint xbits = value._value; + uint ybits = sign._value; + + // Remove the sign from x, and remove everything but the sign from y + // Then, simply OR them to get the correct sign + return new BFloat16((ushort)((xbits & ~SignMask) | (ybits & SignMask))); + } + + /// + public static BFloat16 Max(BFloat16 x, BFloat16 y) => (BFloat16)MathF.Max((float)x, (float)y); + + /// + public static BFloat16 MaxNumber(BFloat16 x, BFloat16 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; + } + + /// + public static BFloat16 Min(BFloat16 x, BFloat16 y) => (BFloat16)MathF.Min((float)x, (float)y); + + /// + public static BFloat16 MinNumber(BFloat16 x, BFloat16 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; + } + + /// + public static int Sign(BFloat16 value) + { + if (IsNaN(value)) + { + throw new ArithmeticException(SR.Arithmetic_NaN); + } + + if (IsZero(value)) + { + return 0; + } + else if (IsNegative(value)) + { + return -1; + } + + return +1; + } + + // + // INumberBase + // + + /// + public static BFloat16 One => new BFloat16(PositiveOneBits); + + /// + static int INumberBase.Radix => 2; + + /// + public static BFloat16 Zero => new BFloat16(PositiveZeroBits); + + /// + public static BFloat16 Abs(BFloat16 value) => new BFloat16((ushort)(value._value & ~SignMask)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BFloat16 CreateChecked(TOther value) + where TOther : INumberBase + { + BFloat16 result; + + if (typeof(TOther) == typeof(BFloat16)) + { + result = (BFloat16)(object)value; + } + else if (!TryConvertFrom(value, out result) && !TOther.TryConvertToChecked(value, out result)) + { + ThrowHelper.ThrowNotSupportedException(); + } + + return result; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BFloat16 CreateSaturating(TOther value) + where TOther : INumberBase + { + BFloat16 result; + + if (typeof(TOther) == typeof(BFloat16)) + { + result = (BFloat16)(object)value; + } + else if (!TryConvertFrom(value, out result) && !TOther.TryConvertToSaturating(value, out result)) + { + ThrowHelper.ThrowNotSupportedException(); + } + + return result; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BFloat16 CreateTruncating(TOther value) + where TOther : INumberBase + { + BFloat16 result; + + if (typeof(TOther) == typeof(BFloat16)) + { + result = (BFloat16)(object)value; + } + else if (!TryConvertFrom(value, out result) && !TOther.TryConvertToTruncating(value, out result)) + { + ThrowHelper.ThrowNotSupportedException(); + } + + return result; + } + + /// + static bool INumberBase.IsCanonical(BFloat16 value) => true; + + /// + static bool INumberBase.IsComplexNumber(BFloat16 value) => false; + + /// + public static bool IsEvenInteger(BFloat16 value) => float.IsEvenInteger((float)value); + + /// + static bool INumberBase.IsImaginaryNumber(BFloat16 value) => false; + + /// + public static bool IsInteger(BFloat16 value) => float.IsInteger((float)value); + + /// + public static bool IsOddInteger(BFloat16 value) => float.IsOddInteger((float)value); + + /// + public static bool IsPositive(BFloat16 value) => (short)(value._value) >= 0; + + /// + public static bool IsRealNumber(BFloat16 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 + } + + /// + static bool INumberBase.IsZero(BFloat16 value) => IsZero(value); + + /// + public static BFloat16 MaxMagnitude(BFloat16 x, BFloat16 y) => (BFloat16)MathF.MaxMagnitude((float)x, (float)y); + + /// + public static BFloat16 MaxMagnitudeNumber(BFloat16 x, BFloat16 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. + + BFloat16 ax = Abs(x); + BFloat16 ay = Abs(y); + + if ((ax > ay) || IsNaN(ay)) + { + return x; + } + + if (ax == ay) + { + return IsNegative(x) ? y : x; + } + + return y; + } + + /// + public static BFloat16 MinMagnitude(BFloat16 x, BFloat16 y) => (BFloat16)MathF.MinMagnitude((float)x, (float)y); + + /// + public static BFloat16 MinMagnitudeNumber(BFloat16 x, BFloat16 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. + + BFloat16 ax = Abs(x); + BFloat16 ay = Abs(y); + + if ((ax < ay) || IsNaN(ay)) + { + return x; + } + + if (ax == ay) + { + return IsNegative(x) ? x : y; + } + + return y; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumberBase.TryConvertFromChecked(TOther value, out BFloat16 result) + { + return TryConvertFrom(value, out result); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumberBase.TryConvertFromSaturating(TOther value, out BFloat16 result) + { + return TryConvertFrom(value, out result); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumberBase.TryConvertFromTruncating(TOther value, out BFloat16 result) + { + return TryConvertFrom(value, out result); + } + + private static bool TryConvertFrom(TOther value, out BFloat16 result) + where TOther : INumberBase + { + // 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 `BFloat16` will handle the other signed types and + // `ConvertTo` will handle the unsigned types + + if (typeof(TOther) == typeof(double)) + { + double actualValue = (double)(object)value; + result = (BFloat16)actualValue; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + short actualValue = (short)(object)value; + result = (BFloat16)actualValue; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + int actualValue = (int)(object)value; + result = (BFloat16)actualValue; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + long actualValue = (long)(object)value; + result = (BFloat16)actualValue; + return true; + } + else if (typeof(TOther) == typeof(Int128)) + { + Int128 actualValue = (Int128)(object)value; + result = (BFloat16)actualValue; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + nint actualValue = (nint)(object)value; + result = (BFloat16)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 = (BFloat16)actualValue; + return true; + } + else if (typeof(TOther) == typeof(Half)) + { + Half actualValue = (Half)(object)value; + result = (BFloat16)actualValue; + return true; + } + else + { + result = default; + return false; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumberBase.TryConvertToChecked(BFloat16 value, [MaybeNullWhen(false)] out TOther result) + { + // `BFloat16` is non-first class type in System.Numerics namespace. + // It should handle all conversions from/to types under System namespace. + + if (typeof(TOther) == typeof(sbyte)) + { + sbyte actualResult = checked((sbyte)value); + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + short actualResult = checked((short)value); + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + int actualResult = checked((int)value); + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + long actualResult = checked((long)value); + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(Int128)) + { + Int128 actualResult = checked((Int128)value); + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + nint actualResult = checked((nint)value); + result = (TOther)(object)actualResult; + return true; + } + 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; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumberBase.TryConvertToSaturating(BFloat16 value, [MaybeNullWhen(false)] out TOther result) + { + return TryConvertTo(value, out result); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool INumberBase.TryConvertToTruncating(BFloat16 value, [MaybeNullWhen(false)] out TOther result) + { + return TryConvertTo(value, out result); + } + + private static bool TryConvertTo(BFloat16 value, [MaybeNullWhen(false)] out TOther result) + where TOther : INumberBase + { + // `BFloat16` is non-first class type in System.Numerics namespace. + // It should handle all conversions from/to types under System namespace. + + if (typeof(TOther) == typeof(sbyte)) + { + sbyte actualResult = (value >= sbyte.MaxValue) ? sbyte.MaxValue : + (value <= sbyte.MinValue) ? sbyte.MinValue : (sbyte)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(short)) + { + short actualResult = ((float)value >= short.MaxValue) ? short.MaxValue : + ((float)value <= short.MinValue) ? short.MinValue : (short)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(int)) + { + int actualResult = ((float)value >= int.MaxValue) ? int.MaxValue : + ((float)value <= int.MinValue) ? int.MinValue : (int)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(long)) + { + long actualResult = ((float)value >= long.MaxValue) ? long.MaxValue : + ((float)value <= long.MinValue) ? long.MinValue : (long)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(Int128)) + { + Int128 actualResult = ((float)value >= +170141183460469231731687303715884105727.0f) ? Int128.MaxValue : + ((float)value <= -170141183460469231731687303715884105728.0f) ? Int128.MinValue : (Int128)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(nint)) + { + nint actualResult = ((float)value >= nint.MaxValue) ? nint.MaxValue : + ((float)value <= nint.MinValue) ? nint.MinValue : (nint)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(byte)) + { + byte actualResult = ((float)value >= byte.MaxValue) ? byte.MaxValue : + ((float)value <= byte.MinValue) ? byte.MinValue : (byte)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(char)) + { + char actualResult = ((float)value >= char.MaxValue) ? char.MaxValue : + (value <= Zero) ? char.MinValue : (char)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(decimal)) + { + decimal actualResult = ((float)value >= +79228162514264337593543950336.0f) ? decimal.MaxValue : + ((float)value <= -79228162514264337593543950336.0f) ? decimal.MinValue : + IsNaN(value) ? 0.0m : (decimal)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(ushort)) + { + ushort actualResult = ((float)value >= ushort.MaxValue) ? ushort.MaxValue : + (value <= Zero) ? ushort.MinValue : (ushort)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(uint)) + { + uint actualResult = ((float)value >= uint.MaxValue) ? uint.MaxValue : + (value <= Zero) ? uint.MinValue : (uint)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(ulong)) + { + ulong actualResult = ((float)value >= ulong.MaxValue) ? ulong.MaxValue : + (value <= Zero) ? ulong.MinValue : (ulong)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(UInt128)) + { + UInt128 actualResult = (value == PositiveInfinity) ? UInt128.MaxValue : + (value <= Zero) ? UInt128.MinValue : (UInt128)value; + result = (TOther)(object)actualResult; + return true; + } + else if (typeof(TOther) == typeof(nuint)) + { + nuint actualResult = ((float)value >= nuint.MaxValue) ? nuint.MaxValue : + (value <= Zero) ? nuint.MinValue : (nuint)value; + result = (TOther)(object)actualResult; + return true; + } + else + { + result = default; + return false; + } + } + + // + // IParsable + // + + /// + public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out BFloat16 result) => TryParse(s, DefaultParseStyle, provider, out result); + + // + // IPowerFunctions + // + + /// + public static BFloat16 Pow(BFloat16 x, BFloat16 y) => (BFloat16)MathF.Pow((float)x, (float)y); + + // + // IRootFunctions + // + + /// + public static BFloat16 Cbrt(BFloat16 x) => (BFloat16)MathF.Cbrt((float)x); + + /// + public static BFloat16 Hypot(BFloat16 x, BFloat16 y) => (BFloat16)float.Hypot((float)x, (float)y); + + /// + public static BFloat16 RootN(BFloat16 x, int n) => (BFloat16)float.RootN((float)x, n); + + /// + public static BFloat16 Sqrt(BFloat16 x) => (BFloat16)MathF.Sqrt((float)x); + + // + // ISignedNumber + // + + /// + public static BFloat16 NegativeOne => new BFloat16(NegativeOneBits); + + // + // ISpanParsable + // + + /// + public static BFloat16 Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, DefaultParseStyle, provider); + + /// + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out BFloat16 result) => TryParse(s, DefaultParseStyle, provider, out result); + + // + // ISubtractionOperators + // + + /// + public static BFloat16 operator -(BFloat16 left, BFloat16 right) => (BFloat16)((float)left - (float)right); + + // + // ITrigonometricFunctions + // + + /// + public static BFloat16 Acos(BFloat16 x) => (BFloat16)MathF.Acos((float)x); + + /// + public static BFloat16 AcosPi(BFloat16 x) => (BFloat16)float.AcosPi((float)x); + + /// + public static BFloat16 Asin(BFloat16 x) => (BFloat16)MathF.Asin((float)x); + + /// + public static BFloat16 AsinPi(BFloat16 x) => (BFloat16)float.AsinPi((float)x); + + /// + public static BFloat16 Atan(BFloat16 x) => (BFloat16)MathF.Atan((float)x); + + /// + public static BFloat16 AtanPi(BFloat16 x) => (BFloat16)float.AtanPi((float)x); + + /// + public static BFloat16 Cos(BFloat16 x) => (BFloat16)MathF.Cos((float)x); + + /// + public static BFloat16 CosPi(BFloat16 x) => (BFloat16)float.CosPi((float)x); + + /// + public static BFloat16 DegreesToRadians(BFloat16 degrees) + { + // NOTE: Don't change the algorithm without consulting the DIM + // which elaborates on why this implementation was chosen + + return (BFloat16)float.DegreesToRadians((float)degrees); + } + + /// + public static BFloat16 RadiansToDegrees(BFloat16 radians) + { + // NOTE: Don't change the algorithm without consulting the DIM + // which elaborates on why this implementation was chosen + + return (BFloat16)float.RadiansToDegrees((float)radians); + } + + /// + public static BFloat16 Sin(BFloat16 x) => (BFloat16)MathF.Sin((float)x); + + /// + public static (BFloat16 Sin, BFloat16 Cos) SinCos(BFloat16 x) + { + var (sin, cos) = MathF.SinCos((float)x); + return ((BFloat16)sin, (BFloat16)cos); + } + + /// + public static (BFloat16 SinPi, BFloat16 CosPi) SinCosPi(BFloat16 x) + { + var (sinPi, cosPi) = float.SinCosPi((float)x); + return ((BFloat16)sinPi, (BFloat16)cosPi); + } + + /// + public static BFloat16 SinPi(BFloat16 x) => (BFloat16)float.SinPi((float)x); + + /// + public static BFloat16 Tan(BFloat16 x) => (BFloat16)MathF.Tan((float)x); + + /// + public static BFloat16 TanPi(BFloat16 x) => (BFloat16)float.TanPi((float)x); + + // + // IUnaryNegationOperators + // + + /// + public static BFloat16 operator -(BFloat16 value) => (BFloat16)(-(float)value); + + // + // IUnaryPlusOperators + // + + /// + public static BFloat16 operator +(BFloat16 value) => value; + + // + // IUtf8SpanParsable + // + + /// + public static BFloat16 Parse(ReadOnlySpan utf8Text, NumberStyles style = NumberStyles.Float | NumberStyles.AllowThousands, IFormatProvider? provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider)); + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out BFloat16 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result); + } + + /// + public static BFloat16 Parse(ReadOnlySpan utf8Text, IFormatProvider? provider) => Parse(utf8Text, NumberStyles.Float | NumberStyles.AllowThousands, provider); + + /// + public static bool TryParse(ReadOnlySpan utf8Text, IFormatProvider? provider, out BFloat16 result) => TryParse(utf8Text, NumberStyles.Float | NumberStyles.AllowThousands, provider, out result); + + // + // IBinaryFloatParseAndFormatInfo + // + + static int IBinaryFloatParseAndFormatInfo.NumberBufferLength => 96 + 1 + 1; // 96 for the longest input + 1 for rounding (+1 for the null terminator) + + static ulong IBinaryFloatParseAndFormatInfo.ZeroBits => 0; + static ulong IBinaryFloatParseAndFormatInfo.InfinityBits => PositiveInfinityBits; + + static ulong IBinaryFloatParseAndFormatInfo.NormalMantissaMask => (1UL << SignificandLength) - 1; + static ulong IBinaryFloatParseAndFormatInfo.DenormalMantissaMask => TrailingSignificandMask; + + static int IBinaryFloatParseAndFormatInfo.MinBinaryExponent => 1 - MaxExponent; + static int IBinaryFloatParseAndFormatInfo.MaxBinaryExponent => MaxExponent; + + static int IBinaryFloatParseAndFormatInfo.MinDecimalExponent => -41; + static int IBinaryFloatParseAndFormatInfo.MaxDecimalExponent => 39; + + static int IBinaryFloatParseAndFormatInfo.ExponentBias => ExponentBias; + static ushort IBinaryFloatParseAndFormatInfo.ExponentBits => BiasedExponentLength; + + static int IBinaryFloatParseAndFormatInfo.OverflowDecimalExponent => (MaxExponent + (2 * SignificandLength)) / 3; + static int IBinaryFloatParseAndFormatInfo.InfinityExponent => MaxBiasedExponent; + + static ushort IBinaryFloatParseAndFormatInfo.NormalMantissaBits => SignificandLength; + static ushort IBinaryFloatParseAndFormatInfo.DenormalMantissaBits => TrailingSignificandLength; + + static int IBinaryFloatParseAndFormatInfo.MinFastFloatDecimalExponent => -59; + static int IBinaryFloatParseAndFormatInfo.MaxFastFloatDecimalExponent => 38; + + static int IBinaryFloatParseAndFormatInfo.MinExponentRoundToEven => -24; + static int IBinaryFloatParseAndFormatInfo.MaxExponentRoundToEven => 3; + + static int IBinaryFloatParseAndFormatInfo.MaxExponentFastPath => 3; + static ulong IBinaryFloatParseAndFormatInfo.MaxMantissaFastPath => 2UL << TrailingSignificandLength; + + static int IBinaryFloatParseAndFormatInfo.MaxRoundTripDigits => 4; + + static int IBinaryFloatParseAndFormatInfo.MaxPrecisionCustomFormat => 4; + + static BFloat16 IBinaryFloatParseAndFormatInfo.BitsToFloat(ulong bits) => new BFloat16((ushort)(bits)); + + static ulong IBinaryFloatParseAndFormatInfo.FloatToBits(BFloat16 value) => value._value; + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index 28c545a65a628..130aa33f3298a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -2174,7 +2174,7 @@ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFo static ushort IBinaryFloatParseAndFormatInfo.NormalMantissaBits => SignificandLength; static ushort IBinaryFloatParseAndFormatInfo.DenormalMantissaBits => TrailingSignificandLength; - static int IBinaryFloatParseAndFormatInfo.MinFastFloatDecimalExponent => -65; + static int IBinaryFloatParseAndFormatInfo.MinFastFloatDecimalExponent => -64; static int IBinaryFloatParseAndFormatInfo.MaxFastFloatDecimalExponent => 38; static int IBinaryFloatParseAndFormatInfo.MinExponentRoundToEven => -17; diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index da44791d9607c..3e7bfe276fccb 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -11128,6 +11128,244 @@ public static void HtmlEncode(string? value, System.IO.TextWriter output) { } } namespace System.Numerics { + public readonly partial struct BFloat16 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryFloatingPointIeee754, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IExponentialFunctions, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IFloatingPointIeee754, System.Numerics.IHyperbolicFunctions, System.Numerics.IIncrementOperators, System.Numerics.ILogarithmicFunctions, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IPowerFunctions, System.Numerics.IRootFunctions, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.ITrigonometricFunctions, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + { + private readonly int _dummyPrimitive; + public static System.Numerics.BFloat16 E { get { throw null; } } + public static System.Numerics.BFloat16 Epsilon { get { throw null; } } + public static System.Numerics.BFloat16 MaxValue { get { throw null; } } + public static System.Numerics.BFloat16 MinValue { get { throw null; } } + public static System.Numerics.BFloat16 MultiplicativeIdentity { get { throw null; } } + public static System.Numerics.BFloat16 NaN { get { throw null; } } + public static System.Numerics.BFloat16 NegativeInfinity { get { throw null; } } + public static System.Numerics.BFloat16 NegativeOne { get { throw null; } } + public static System.Numerics.BFloat16 NegativeZero { get { throw null; } } + public static System.Numerics.BFloat16 One { get { throw null; } } + public static System.Numerics.BFloat16 Pi { get { throw null; } } + public static System.Numerics.BFloat16 PositiveInfinity { get { throw null; } } + static System.Numerics.BFloat16 System.Numerics.IAdditiveIdentity.AdditiveIdentity { get { throw null; } } + static System.Numerics.BFloat16 System.Numerics.IBinaryNumber.AllBitsSet { get { throw null; } } + static int System.Numerics.INumberBase.Radix { get { throw null; } } + public static System.Numerics.BFloat16 Tau { get { throw null; } } + public static System.Numerics.BFloat16 Zero { get { throw null; } } + public static System.Numerics.BFloat16 Abs(System.Numerics.BFloat16 value) { throw null; } + public static System.Numerics.BFloat16 Acos(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Acosh(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 AcosPi(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Asin(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Asinh(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 AsinPi(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Atan(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Atan2(System.Numerics.BFloat16 y, System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Atan2Pi(System.Numerics.BFloat16 y, System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Atanh(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 AtanPi(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 BitDecrement(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 BitIncrement(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Cbrt(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Ceiling(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Clamp(System.Numerics.BFloat16 value, System.Numerics.BFloat16 min, System.Numerics.BFloat16 max) { throw null; } + public int CompareTo(System.Numerics.BFloat16 other) { throw null; } + public int CompareTo(object? obj) { throw null; } + public static System.Numerics.BFloat16 CopySign(System.Numerics.BFloat16 value, System.Numerics.BFloat16 sign) { throw null; } + public static System.Numerics.BFloat16 Cos(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Cosh(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 CosPi(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 CreateChecked(TOther value) where TOther : System.Numerics.INumberBase { throw null; } + public static System.Numerics.BFloat16 CreateSaturating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } + public static System.Numerics.BFloat16 CreateTruncating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } + public static System.Numerics.BFloat16 DegreesToRadians(System.Numerics.BFloat16 degrees) { throw null; } + public bool Equals(System.Numerics.BFloat16 other) { throw null; } + public override bool Equals(object? obj) { throw null; } + public static System.Numerics.BFloat16 Exp(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Exp10(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Exp10M1(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Exp2(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Exp2M1(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 ExpM1(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Floor(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 FusedMultiplyAdd(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right, System.Numerics.BFloat16 addend) { throw null; } + public override int GetHashCode() { throw null; } + public static System.Numerics.BFloat16 Hypot(System.Numerics.BFloat16 x, System.Numerics.BFloat16 y) { throw null; } + public static System.Numerics.BFloat16 Ieee754Remainder(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static int ILogB(System.Numerics.BFloat16 x) { throw null; } + public static bool IsEvenInteger(System.Numerics.BFloat16 value) { throw null; } + public static bool IsFinite(System.Numerics.BFloat16 value) { throw null; } + public static bool IsInfinity(System.Numerics.BFloat16 value) { throw null; } + public static bool IsInteger(System.Numerics.BFloat16 value) { throw null; } + public static bool IsNaN(System.Numerics.BFloat16 value) { throw null; } + public static bool IsNegative(System.Numerics.BFloat16 value) { throw null; } + public static bool IsNegativeInfinity(System.Numerics.BFloat16 value) { throw null; } + public static bool IsNormal(System.Numerics.BFloat16 value) { throw null; } + public static bool IsOddInteger(System.Numerics.BFloat16 value) { throw null; } + public static bool IsPositive(System.Numerics.BFloat16 value) { throw null; } + public static bool IsPositiveInfinity(System.Numerics.BFloat16 value) { throw null; } + public static bool IsPow2(System.Numerics.BFloat16 value) { throw null; } + public static bool IsRealNumber(System.Numerics.BFloat16 value) { throw null; } + public static bool IsSubnormal(System.Numerics.BFloat16 value) { throw null; } + public static bool IsZero(System.Numerics.BFloat16 value) { throw null; } + public static System.Numerics.BFloat16 Lerp(System.Numerics.BFloat16 value1, System.Numerics.BFloat16 value2, System.Numerics.BFloat16 amount) { throw null; } + public static System.Numerics.BFloat16 Log(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Log(System.Numerics.BFloat16 x, System.Numerics.BFloat16 newBase) { throw null; } + public static System.Numerics.BFloat16 Log10(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Log10P1(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Log2(System.Numerics.BFloat16 value) { throw null; } + public static System.Numerics.BFloat16 Log2P1(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 LogP1(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Max(System.Numerics.BFloat16 x, System.Numerics.BFloat16 y) { throw null; } + public static System.Numerics.BFloat16 MaxMagnitude(System.Numerics.BFloat16 x, System.Numerics.BFloat16 y) { throw null; } + public static System.Numerics.BFloat16 MaxMagnitudeNumber(System.Numerics.BFloat16 x, System.Numerics.BFloat16 y) { throw null; } + public static System.Numerics.BFloat16 MaxNumber(System.Numerics.BFloat16 x, System.Numerics.BFloat16 y) { throw null; } + public static System.Numerics.BFloat16 Min(System.Numerics.BFloat16 x, System.Numerics.BFloat16 y) { throw null; } + public static System.Numerics.BFloat16 MinMagnitude(System.Numerics.BFloat16 x, System.Numerics.BFloat16 y) { throw null; } + public static System.Numerics.BFloat16 MinMagnitudeNumber(System.Numerics.BFloat16 x, System.Numerics.BFloat16 y) { throw null; } + public static System.Numerics.BFloat16 MinNumber(System.Numerics.BFloat16 x, System.Numerics.BFloat16 y) { throw null; } + public static System.Numerics.BFloat16 operator +(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static explicit operator checked byte (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator checked char (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator checked short (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator checked int (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator checked long (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator checked System.Int128 (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator checked nint (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator checked sbyte (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator checked ushort (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator checked uint (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator checked ulong (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator checked System.UInt128 (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator checked nuint (System.Numerics.BFloat16 value) { throw null; } + public static System.Numerics.BFloat16 operator --(System.Numerics.BFloat16 value) { throw null; } + public static System.Numerics.BFloat16 operator /(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static bool operator ==(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static explicit operator System.Numerics.BFloat16 (char value) { throw null; } + public static explicit operator System.Numerics.BFloat16 (decimal value) { throw null; } + public static explicit operator System.Numerics.BFloat16 (double value) { throw null; } + public static explicit operator System.Numerics.BFloat16 (System.Half value) { throw null; } + public static explicit operator System.Numerics.BFloat16 (System.Int128 value) { throw null; } + public static explicit operator System.Numerics.BFloat16 (short value) { throw null; } + public static explicit operator System.Numerics.BFloat16 (int value) { throw null; } + public static explicit operator System.Numerics.BFloat16 (long value) { throw null; } + public static explicit operator System.Numerics.BFloat16 (nint value) { throw null; } + public static explicit operator byte (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator char (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator decimal (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator double (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator System.Int128 (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator short (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator int (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator long (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator nint (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator sbyte (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator float (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator System.UInt128 (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator ushort (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator uint (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator ulong (System.Numerics.BFloat16 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator nuint (System.Numerics.BFloat16 value) { throw null; } + public static explicit operator System.Numerics.BFloat16 (float value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator System.Numerics.BFloat16 (System.UInt128 value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator System.Numerics.BFloat16 (ushort value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator System.Numerics.BFloat16 (uint value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator System.Numerics.BFloat16 (ulong value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static explicit operator System.Numerics.BFloat16 (nuint value) { throw null; } + public static bool operator >(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static bool operator >=(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static implicit operator System.Numerics.BFloat16 (byte value) { throw null; } + [System.CLSCompliantAttribute(false)] + public static implicit operator System.Numerics.BFloat16 (sbyte value) { throw null; } + public static System.Numerics.BFloat16 operator ++(System.Numerics.BFloat16 value) { throw null; } + public static bool operator !=(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static bool operator <(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static bool operator <=(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static System.Numerics.BFloat16 operator %(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static System.Numerics.BFloat16 operator *(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static System.Numerics.BFloat16 operator -(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + public static System.Numerics.BFloat16 operator -(System.Numerics.BFloat16 value) { throw null; } + public static System.Numerics.BFloat16 operator +(System.Numerics.BFloat16 value) { throw null; } + public static System.Numerics.BFloat16 Parse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowLeadingSign | System.Globalization.NumberStyles.AllowLeadingWhite | System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.AllowTrailingWhite, System.IFormatProvider? provider = null) { throw null; } + public static System.Numerics.BFloat16 Parse(System.ReadOnlySpan utf8Text, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.BFloat16 Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowLeadingSign | System.Globalization.NumberStyles.AllowLeadingWhite | System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.AllowTrailingWhite, System.IFormatProvider? provider = null) { throw null; } + public static System.Numerics.BFloat16 Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.BFloat16 Parse(string s) { throw null; } + public static System.Numerics.BFloat16 Parse(string s, System.Globalization.NumberStyles style) { throw null; } + public static System.Numerics.BFloat16 Parse(string s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowLeadingSign | System.Globalization.NumberStyles.AllowLeadingWhite | System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.AllowTrailingWhite, System.IFormatProvider? provider = null) { throw null; } + public static System.Numerics.BFloat16 Parse(string s, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.BFloat16 Pow(System.Numerics.BFloat16 x, System.Numerics.BFloat16 y) { throw null; } + public static System.Numerics.BFloat16 RadiansToDegrees(System.Numerics.BFloat16 radians) { throw null; } + public static System.Numerics.BFloat16 ReciprocalEstimate(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 ReciprocalSqrtEstimate(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 RootN(System.Numerics.BFloat16 x, int n) { throw null; } + public static System.Numerics.BFloat16 Round(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Round(System.Numerics.BFloat16 x, int digits) { throw null; } + public static System.Numerics.BFloat16 Round(System.Numerics.BFloat16 x, int digits, System.MidpointRounding mode) { throw null; } + public static System.Numerics.BFloat16 Round(System.Numerics.BFloat16 x, System.MidpointRounding mode) { throw null; } + public static System.Numerics.BFloat16 ScaleB(System.Numerics.BFloat16 x, int n) { throw null; } + public static int Sign(System.Numerics.BFloat16 value) { throw null; } + public static System.Numerics.BFloat16 Sin(System.Numerics.BFloat16 x) { throw null; } + public static (System.Numerics.BFloat16 Sin, System.Numerics.BFloat16 Cos) SinCos(System.Numerics.BFloat16 x) { throw null; } + public static (System.Numerics.BFloat16 SinPi, System.Numerics.BFloat16 CosPi) SinCosPi(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Sinh(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 SinPi(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Sqrt(System.Numerics.BFloat16 x) { throw null; } + static System.Numerics.BFloat16 System.Numerics.IBitwiseOperators.operator &(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + static System.Numerics.BFloat16 System.Numerics.IBitwiseOperators.operator |(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + static System.Numerics.BFloat16 System.Numerics.IBitwiseOperators.operator ^(System.Numerics.BFloat16 left, System.Numerics.BFloat16 right) { throw null; } + static System.Numerics.BFloat16 System.Numerics.IBitwiseOperators.operator ~(System.Numerics.BFloat16 value) { throw null; } + int System.Numerics.IFloatingPoint.GetExponentByteCount() { throw null; } + int System.Numerics.IFloatingPoint.GetExponentShortestBitLength() { throw null; } + int System.Numerics.IFloatingPoint.GetSignificandBitLength() { throw null; } + int System.Numerics.IFloatingPoint.GetSignificandByteCount() { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteExponentBigEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteExponentLittleEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteSignificandBigEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteSignificandLittleEndian(System.Span destination, out int bytesWritten) { throw null; } + static bool System.Numerics.INumberBase.IsCanonical(System.Numerics.BFloat16 value) { throw null; } + static bool System.Numerics.INumberBase.IsComplexNumber(System.Numerics.BFloat16 value) { throw null; } + static bool System.Numerics.INumberBase.IsImaginaryNumber(System.Numerics.BFloat16 value) { throw null; } + static bool System.Numerics.INumberBase.IsZero(System.Numerics.BFloat16 value) { throw null; } + static bool System.Numerics.INumberBase.TryConvertFromChecked(TOther value, out System.Numerics.BFloat16 result) { throw null; } + static bool System.Numerics.INumberBase.TryConvertFromSaturating(TOther value, out System.Numerics.BFloat16 result) { throw null; } + static bool System.Numerics.INumberBase.TryConvertFromTruncating(TOther value, out System.Numerics.BFloat16 result) { throw null; } + static bool System.Numerics.INumberBase.TryConvertToChecked(System.Numerics.BFloat16 value, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TOther result) { throw null; } + static bool System.Numerics.INumberBase.TryConvertToSaturating(System.Numerics.BFloat16 value, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TOther result) { throw null; } + static bool System.Numerics.INumberBase.TryConvertToTruncating(System.Numerics.BFloat16 value, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TOther result) { throw null; } + public static System.Numerics.BFloat16 Tan(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 Tanh(System.Numerics.BFloat16 x) { throw null; } + public static System.Numerics.BFloat16 TanPi(System.Numerics.BFloat16 x) { throw null; } + public override string ToString() { throw null; } + public string ToString(System.IFormatProvider? provider) { throw null; } + public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format) { throw null; } + public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.BFloat16 Truncate(System.Numerics.BFloat16 x) { throw null; } + public bool TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.BFloat16 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.IFormatProvider? provider, out System.Numerics.BFloat16 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, out System.Numerics.BFloat16 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.BFloat16 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.Numerics.BFloat16 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, out System.Numerics.BFloat16 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.BFloat16 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.Numerics.BFloat16 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Numerics.BFloat16 result) { throw null; } + } public static partial class BitOperations { [System.CLSCompliantAttribute(false)] diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj index fdb5ae06eba71..58e53d776b7a3 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj @@ -138,6 +138,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs index 9ad2869ba5a63..d4f7475debe47 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs @@ -1692,17 +1692,17 @@ public static IEnumerable Hypot_TestData() [Theory] [MemberData(nameof(Hypot_TestData))] - public static void Hypot(float x, float y, float expectedResult, float allowedVariance) + public static void Hypot(Half x, Half y, Half expectedResult, Half allowedVariance) { - AssertExtensions.Equal(expectedResult, float.Hypot(-x, -y), allowedVariance); - AssertExtensions.Equal(expectedResult, float.Hypot(-x, +y), allowedVariance); - AssertExtensions.Equal(expectedResult, float.Hypot(+x, -y), allowedVariance); - AssertExtensions.Equal(expectedResult, float.Hypot(+x, +y), allowedVariance); + AssertExtensions.Equal(expectedResult, Half.Hypot(-x, -y), allowedVariance); + AssertExtensions.Equal(expectedResult, Half.Hypot(-x, +y), allowedVariance); + AssertExtensions.Equal(expectedResult, Half.Hypot(+x, -y), allowedVariance); + AssertExtensions.Equal(expectedResult, Half.Hypot(+x, +y), allowedVariance); - AssertExtensions.Equal(expectedResult, float.Hypot(-y, -x), allowedVariance); - AssertExtensions.Equal(expectedResult, float.Hypot(-y, +x), allowedVariance); - AssertExtensions.Equal(expectedResult, float.Hypot(+y, -x), allowedVariance); - AssertExtensions.Equal(expectedResult, float.Hypot(+y, +x), allowedVariance); + AssertExtensions.Equal(expectedResult, Half.Hypot(-y, -x), allowedVariance); + AssertExtensions.Equal(expectedResult, Half.Hypot(-y, +x), allowedVariance); + AssertExtensions.Equal(expectedResult, Half.Hypot(+y, -x), allowedVariance); + AssertExtensions.Equal(expectedResult, Half.Hypot(+y, +x), allowedVariance); } public static IEnumerable RootN_TestData() diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/BFloat16Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/BFloat16Tests.cs new file mode 100644 index 0000000000000..af764c7c887ba --- /dev/null +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/BFloat16Tests.cs @@ -0,0 +1,2384 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Tests; +using System.Text; +using Xunit; +using Xunit.Sdk; + +namespace System.Numerics.Tests +{ + public class BFloat16Tests + { + private static BFloat16 CrossPlatformMachineEpsilon => (BFloat16)3.90625e-03f; + + private static ushort BFloat16ToUInt16Bits(BFloat16 value) => Unsafe.BitCast(value); + + private static BFloat16 UInt16BitsToBFloat16(ushort value) => Unsafe.BitCast(value); + + [Fact] + public static void Epsilon() + { + Assert.Equal(0x0001u, BFloat16ToUInt16Bits(BFloat16.Epsilon)); + } + + [Fact] + public static void PositiveInfinity() + { + Assert.Equal(0x7F80u, BFloat16ToUInt16Bits(BFloat16.PositiveInfinity)); + } + + [Fact] + public static void NegativeInfinity() + { + Assert.Equal(0xFF80u, BFloat16ToUInt16Bits(BFloat16.NegativeInfinity)); + } + + [Fact] + public static void NaN() + { + Assert.Equal(0xFFC0u, BFloat16ToUInt16Bits(BFloat16.NaN)); + } + + [Fact] + public static void MinValue() + { + Assert.Equal(0xFF7Fu, BFloat16ToUInt16Bits(BFloat16.MinValue)); + } + + [Fact] + public static void MaxValue() + { + Assert.Equal(0x7F7Fu, BFloat16ToUInt16Bits(BFloat16.MaxValue)); + } + + [Fact] + public static void Ctor_Empty() + { + var value = new BFloat16(); + Assert.Equal(0x0000, BFloat16ToUInt16Bits(value)); + } + + public static IEnumerable IsFinite_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, false }; // Negative Infinity + yield return new object[] { BFloat16.MinValue, true }; // Min Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x8400), true }; // Max Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x83FF), true }; // Min Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8001), true }; // Max Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8000), true }; // Negative Zero + yield return new object[] { BFloat16.NaN, false }; // NaN + yield return new object[] { UInt16BitsToBFloat16(0x0000), true }; // Positive Zero + yield return new object[] { BFloat16.Epsilon, true }; // Min Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x03FF), true }; // Max Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x0400), true }; // Min Positive Normal + yield return new object[] { BFloat16.MaxValue, true }; // Max Positive Normal + yield return new object[] { BFloat16.PositiveInfinity, false }; // Positive Infinity + } + + [Theory] + [MemberData(nameof(IsFinite_TestData))] + public static void IsFinite(BFloat16 value, bool expected) + { + Assert.Equal(expected, BFloat16.IsFinite(value)); + } + + public static IEnumerable IsInfinity_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, true }; // Negative Infinity + yield return new object[] { BFloat16.MinValue, false }; // Min Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x8080), false }; // Max Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x807F), false }; // Min Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8001), false }; // Max Negative Subnormal (Negative Epsilon) + yield return new object[] { UInt16BitsToBFloat16(0x8000), false }; // Negative Zero + yield return new object[] { BFloat16.NaN, false }; // NaN + yield return new object[] { UInt16BitsToBFloat16(0x0000), false }; // Positive Zero + yield return new object[] { BFloat16.Epsilon, false }; // Min Positive Subnormal (Positive Epsilon) + yield return new object[] { UInt16BitsToBFloat16(0x007F), false }; // Max Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x0080), false }; // Min Positive Normal + yield return new object[] { BFloat16.MaxValue, false }; // Max Positive Normal + yield return new object[] { BFloat16.PositiveInfinity, true }; // Positive Infinity + } + + [Theory] + [MemberData(nameof(IsInfinity_TestData))] + public static void IsInfinity(BFloat16 value, bool expected) + { + Assert.Equal(expected, BFloat16.IsInfinity(value)); + } + + public static IEnumerable IsNaN_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, false }; // Negative Infinity + yield return new object[] { BFloat16.MinValue, false }; // Min Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x8080), false }; // Max Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x807F), false }; // Min Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8001), false }; // Max Negative Subnormal (Negative Epsilon) + yield return new object[] { UInt16BitsToBFloat16(0x8000), false }; // Negative Zero + yield return new object[] { BFloat16.NaN, true }; // NaN + yield return new object[] { UInt16BitsToBFloat16(0x0000), false }; // Positive Zero + yield return new object[] { BFloat16.Epsilon, false }; // Min Positive Subnormal (Positive Epsilon) + yield return new object[] { UInt16BitsToBFloat16(0x007F), false }; // Max Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x0080), false }; // Min Positive Normal + yield return new object[] { BFloat16.MaxValue, false }; // Max Positive Normal + yield return new object[] { BFloat16.PositiveInfinity, false }; // Positive Infinity + } + + [Theory] + [MemberData(nameof(IsNaN_TestData))] + public static void IsNaN(BFloat16 value, bool expected) + { + Assert.Equal(expected, BFloat16.IsNaN(value)); + } + + public static IEnumerable IsNegative_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, true }; // Negative Infinity + yield return new object[] { BFloat16.MinValue, true }; // Min Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x8080), true }; // Max Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x807F), true }; // Min Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8001), true }; // Max Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8000), true }; // Negative Zero + yield return new object[] { BFloat16.NaN, true }; // NaN + yield return new object[] { UInt16BitsToBFloat16(0x0000), false }; // Positive Zero + yield return new object[] { BFloat16.Epsilon, false }; // Min Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x007F), false }; // Max Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x0080), false }; // Min Positive Normal + yield return new object[] { BFloat16.MaxValue, false }; // Max Positive Normal + yield return new object[] { BFloat16.PositiveInfinity, false }; // Positive Infinity + } + + [Theory] + [MemberData(nameof(IsNegative_TestData))] + public static void IsNegative(BFloat16 value, bool expected) + { + Assert.Equal(expected, BFloat16.IsNegative(value)); + } + + public static IEnumerable IsNegativeInfinity_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, true }; // Negative Infinity + yield return new object[] { BFloat16.MinValue, false }; // Min Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x8080), false }; // Max Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x807F), false }; // Min Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8001), false }; // Max Negative Subnormal (Negative Epsilon) + yield return new object[] { UInt16BitsToBFloat16(0x8000), false }; // Negative Zero + yield return new object[] { BFloat16.NaN, false }; // NaN + yield return new object[] { UInt16BitsToBFloat16(0x0000), false }; // Positive Zero + yield return new object[] { BFloat16.Epsilon, false }; // Min Positive Subnormal (Positive Epsilon) + yield return new object[] { UInt16BitsToBFloat16(0x007F), false }; // Max Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x0080), false }; // Min Positive Normal + yield return new object[] { BFloat16.MaxValue, false }; // Max Positive Normal + yield return new object[] { BFloat16.PositiveInfinity, false }; // Positive Infinity + } + + [Theory] + [MemberData(nameof(IsNegativeInfinity_TestData))] + public static void IsNegativeInfinity(BFloat16 value, bool expected) + { + Assert.Equal(expected, BFloat16.IsNegativeInfinity(value)); + } + + public static IEnumerable IsNormal_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, false }; // Negative Infinity + yield return new object[] { BFloat16.MinValue, true }; // Min Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x8080), true }; // Max Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x807F), false }; // Min Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8001), false }; // Max Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8000), false }; // Negative Zero + yield return new object[] { BFloat16.NaN, false }; // NaN + yield return new object[] { UInt16BitsToBFloat16(0x0000), false }; // Positive Zero + yield return new object[] { BFloat16.Epsilon, false }; // Min Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x007F), false }; // Max Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x0080), true }; // Min Positive Normal + yield return new object[] { BFloat16.MaxValue, true }; // Max Positive Normal + yield return new object[] { BFloat16.PositiveInfinity, false }; // Positive Infinity + } + + [Theory] + [MemberData(nameof(IsNormal_TestData))] + public static void IsNormal(BFloat16 value, bool expected) + { + Assert.Equal(expected, BFloat16.IsNormal(value)); + } + + public static IEnumerable IsPositiveInfinity_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, false }; // Negative Infinity + yield return new object[] { BFloat16.MinValue, false }; // Min Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x8080), false }; // Max Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x807F), false }; // Min Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8001), false }; // Max Negative Subnormal (Negative Epsilon) + yield return new object[] { UInt16BitsToBFloat16(0x8000), false }; // Negative Zero + yield return new object[] { BFloat16.NaN, false }; // NaN + yield return new object[] { UInt16BitsToBFloat16(0x0000), false }; // Positive Zero + yield return new object[] { BFloat16.Epsilon, false }; // Min Positive Subnormal (Positive Epsilon) + yield return new object[] { UInt16BitsToBFloat16(0x007F), false }; // Max Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x0080), false }; // Min Positive Normal + yield return new object[] { BFloat16.MaxValue, false }; // Max Positive Normal + yield return new object[] { BFloat16.PositiveInfinity, true }; // Positive Infinity + } + + [Theory] + [MemberData(nameof(IsPositiveInfinity_TestData))] + public static void IsPositiveInfinity(BFloat16 value, bool expected) + { + Assert.Equal(expected, BFloat16.IsPositiveInfinity(value)); + } + + public static IEnumerable IsSubnormal_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, false }; // Negative Infinity + yield return new object[] { BFloat16.MinValue, false }; // Min Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x8080), false }; // Max Negative Normal + yield return new object[] { UInt16BitsToBFloat16(0x807F), true }; // Min Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8001), true }; // Max Negative Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x8000), false }; // Negative Zero + yield return new object[] { BFloat16.NaN, false }; // NaN + yield return new object[] { UInt16BitsToBFloat16(0x0000), false }; // Positive Zero + yield return new object[] { BFloat16.Epsilon, true }; // Min Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x007F), true }; // Max Positive Subnormal + yield return new object[] { UInt16BitsToBFloat16(0x0080), false }; // Min Positive Normal + yield return new object[] { BFloat16.MaxValue, false }; // Max Positive Normal + yield return new object[] { BFloat16.PositiveInfinity, false }; // Positive Infinity + } + + [Theory] + [MemberData(nameof(IsSubnormal_TestData))] + public static void IsSubnormal(BFloat16 value, bool expected) + { + Assert.Equal(expected, BFloat16.IsSubnormal(value)); + } + + public static IEnumerable CompareTo_ThrowsArgumentException_TestData() + { + yield return new object[] { "a" }; + yield return new object[] { 234.0 }; + } + + [Theory] + [MemberData(nameof(CompareTo_ThrowsArgumentException_TestData))] + public static void CompareTo_ThrowsArgumentException(object obj) + { + Assert.Throws(() => BFloat16.MaxValue.CompareTo(obj)); + } + + public static IEnumerable CompareTo_TestData() + { + yield return new object[] { BFloat16.MaxValue, BFloat16.MaxValue, 0 }; + yield return new object[] { BFloat16.MaxValue, BFloat16.MinValue, 1 }; + yield return new object[] { BFloat16.Epsilon, UInt16BitsToBFloat16(0x8001), 1 }; + yield return new object[] { BFloat16.MaxValue, UInt16BitsToBFloat16(0x0000), 1 }; + yield return new object[] { BFloat16.MaxValue, BFloat16.Epsilon, 1 }; + yield return new object[] { BFloat16.MaxValue, BFloat16.PositiveInfinity, -1 }; + yield return new object[] { BFloat16.MinValue, BFloat16.MaxValue, -1 }; + yield return new object[] { BFloat16.MaxValue, BFloat16.NaN, 1 }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, 0 }; + yield return new object[] { BFloat16.NaN, UInt16BitsToBFloat16(0x0000), -1 }; + yield return new object[] { BFloat16.MaxValue, null, 1 }; + yield return new object[] { BFloat16.MinValue, BFloat16.NegativeInfinity, 1 }; + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.MinValue, -1 }; + yield return new object[] { UInt16BitsToBFloat16(0x8000), BFloat16.NegativeInfinity, 1 }; // Negative zero + yield return new object[] { BFloat16.NegativeInfinity, UInt16BitsToBFloat16(0x8000), -1 }; // Negative zero + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.NegativeInfinity, 0 }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, 0 }; + yield return new object[] { (BFloat16)(-180f), (BFloat16)(-180f), 0 }; + yield return new object[] { (BFloat16)(180f), (BFloat16)(180f), 0 }; + yield return new object[] { (BFloat16)(-180f), (BFloat16)(180f), -1 }; + yield return new object[] { (BFloat16)(180f), (BFloat16)(-180f), 1 }; + yield return new object[] { (BFloat16)(-65535), (object)null, 1 }; + } + + [Theory] + [MemberData(nameof(CompareTo_TestData))] + public static void CompareTo(BFloat16 value, object obj, int expected) + { + if (obj is BFloat16 other) + { + Assert.Equal(expected, Math.Sign(value.CompareTo(other))); + + if (BFloat16.IsNaN(value) || BFloat16.IsNaN(other)) + { + Assert.False(value >= other); + Assert.False(value > other); + Assert.False(value <= other); + Assert.False(value < other); + } + else + { + if (expected >= 0) + { + Assert.True(value >= other); + Assert.False(value < other); + } + if (expected > 0) + { + Assert.True(value > other); + Assert.False(value <= other); + } + if (expected <= 0) + { + Assert.True(value <= other); + Assert.False(value > other); + } + if (expected < 0) + { + Assert.True(value < other); + Assert.False(value >= other); + } + } + } + + Assert.Equal(expected, Math.Sign(value.CompareTo(obj))); + } + + public static IEnumerable Equals_TestData() + { + yield return new object[] { BFloat16.MaxValue, BFloat16.MaxValue, true }; + yield return new object[] { BFloat16.MaxValue, BFloat16.MinValue, false }; + yield return new object[] { BFloat16.MaxValue, UInt16BitsToBFloat16(0x0000), false }; + yield return new object[] { BFloat16.MaxValue, 789.0f, false }; + yield return new object[] { BFloat16.MaxValue, "789", false }; + } + + [Theory] + [MemberData(nameof(Equals_TestData))] + public static void EqualsTest(BFloat16 value, object obj, bool expected) + { + Assert.Equal(expected, value.Equals(obj)); + } + + public static IEnumerable ExplicitConversion_ToSingle_TestData() + { + (BFloat16 Original, float Expected)[] data = // Fraction is truncated for lower 16 bits + { + (UInt16BitsToBFloat16(0b0_01111111_0000000), 1f), // 1 + (UInt16BitsToBFloat16(0b1_01111111_0000000), -1f), // -1 + (BFloat16.MaxValue, BitConverter.UInt32BitsToSingle(0x7F7F0000)), // 3.3895314E+38 + (BFloat16.MinValue, BitConverter.UInt32BitsToSingle(0xFF7F0000)), // -3.3895314E+38 + (UInt16BitsToBFloat16(0b0_01111011_1001101), 0.10009765625f), // 0.1ish + (UInt16BitsToBFloat16(0b1_01111011_1001101), -0.10009765625f), // -0.1ish + (UInt16BitsToBFloat16(0b0_10000100_0101000), 42f), // 42 + (UInt16BitsToBFloat16(0b1_10000100_0101000), -42f), // -42 + (BFloat16.PositiveInfinity, float.PositiveInfinity), // PosInfinity + (BFloat16.NegativeInfinity, float.NegativeInfinity), // NegInfinity + (UInt16BitsToBFloat16(0b0_11111111_1000000), BitConverter.UInt32BitsToSingle(0x7FC00000)), // Positive Quiet NaN + (BFloat16.NaN, float.NaN), // Negative Quiet NaN + (UInt16BitsToBFloat16(0b0_11111111_1010101), BitConverter.UInt32BitsToSingle(0x7FD50000)), // Positive Signalling NaN - Should preserve payload + (UInt16BitsToBFloat16(0b1_11111111_1010101), BitConverter.UInt32BitsToSingle(0xFFD50000)), // Negative Signalling NaN - Should preserve payload + (BFloat16.Epsilon, BitConverter.UInt32BitsToSingle(0x00010000)), // PosEpsilon = 9.1835E-41 + (UInt16BitsToBFloat16(0), 0), // 0 + (UInt16BitsToBFloat16(0b1_00000000_0000000), -0f), // -0 + (UInt16BitsToBFloat16(0b0_10000000_1001001), 3.140625f), // 3.140625 + (UInt16BitsToBFloat16(0b1_10000000_1001001), -3.140625f), // -3.140625 + (UInt16BitsToBFloat16(0b0_10000000_0101110), 2.71875f), // 2.71875 + (UInt16BitsToBFloat16(0b1_10000000_0101110), -2.71875f), // -2.71875 + (UInt16BitsToBFloat16(0b0_01111111_1000000), 1.5f), // 1.5 + (UInt16BitsToBFloat16(0b1_01111111_1000000), -1.5f), // -1.5 + (UInt16BitsToBFloat16(0b0_01111111_1000001), 1.5078125f), // 1.5078125 + (UInt16BitsToBFloat16(0b1_01111111_1000001), -1.5078125f), // -1.5078125 + (UInt16BitsToBFloat16(0b0_00000001_0000000), BitConverter.UInt32BitsToSingle(0x00800000)), // smallest normal + (UInt16BitsToBFloat16(0b0_00000000_1111111), BitConverter.UInt32BitsToSingle(0x007F0000)), // largest subnormal + (UInt16BitsToBFloat16(0b0_00000000_1000000), BitConverter.UInt32BitsToSingle(0x00400000)), // middle subnormal + (UInt16BitsToBFloat16(0b0_00000000_0111111), BitConverter.UInt32BitsToSingle(0x003F0000)), // just below middle subnormal + (UInt16BitsToBFloat16(0b0_00000000_0000001), BitConverter.UInt32BitsToSingle(0x00010000)), // smallest subnormal + (UInt16BitsToBFloat16(0b1_00000000_0000001), BitConverter.UInt32BitsToSingle(0x80010000)), // highest negative subnormal + (UInt16BitsToBFloat16(0b1_00000000_0111111), BitConverter.UInt32BitsToSingle(0x803F0000)), // just above negative middle subnormal + (UInt16BitsToBFloat16(0b1_00000000_1000000), BitConverter.UInt32BitsToSingle(0x80400000)), // negative middle subnormal + (UInt16BitsToBFloat16(0b1_00000000_1111111), BitConverter.UInt32BitsToSingle(0x807F0000)), // lowest negative subnormal + (UInt16BitsToBFloat16(0b1_00000001_0000000), BitConverter.UInt32BitsToSingle(0x80800000)) // highest negative normal + }; + + foreach ((BFloat16 original, float expected) in data) + { + yield return new object[] { original, expected }; + } + } + + [MemberData(nameof(ExplicitConversion_ToSingle_TestData))] + [Theory] + public static void ExplicitConversion_ToSingle(BFloat16 value, float expected) // Check the underlying bits for verifying NaNs + { + float f = (float)value; + AssertExtensions.Equal(expected, f); + } + + public static IEnumerable ExplicitConversion_ToDouble_TestData() + { + (BFloat16 Original, double Expected)[] data = + { + (UInt16BitsToBFloat16(0b0_01111111_0000000), 1d), // 1 + (UInt16BitsToBFloat16(0b1_01111111_0000000), -1d), // -1 + (BFloat16.MaxValue, BitConverter.UInt64BitsToDouble(0x47EFE000_00000000)), // 3.3895314E+38 + (BFloat16.MinValue, BitConverter.UInt64BitsToDouble(0xC7EFE000_00000000)), // -3.3895314E+38 + (UInt16BitsToBFloat16(0b0_01111011_1001101), 0.10009765625d), // 0.1ish + (UInt16BitsToBFloat16(0b1_01111011_1001101), -0.10009765625d), // -0.1ish + (UInt16BitsToBFloat16(0b0_10000100_0101000), 42d), // 42 + (UInt16BitsToBFloat16(0b1_10000100_0101000), -42d), // -42 + (BFloat16.PositiveInfinity, double.PositiveInfinity), // PosInfinity + (BFloat16.NegativeInfinity, double.NegativeInfinity), // NegInfinity + (UInt16BitsToBFloat16(0b0_11111111_1000000), BitConverter.UInt64BitsToDouble(0x7FF80000_00000000)), // Positive Quiet NaN + (BFloat16.NaN, double.NaN), // Negative Quiet NaN + (UInt16BitsToBFloat16(0b0_11111111_1010101), BitConverter.UInt64BitsToDouble(0x7FFAA000_00000000)), // Positive Signalling NaN - Should preserve payload + (UInt16BitsToBFloat16(0b1_11111111_1010101), BitConverter.UInt64BitsToDouble(0xFFFAA000_00000000)), // Negative Signalling NaN - Should preserve payload + (BFloat16.Epsilon, BitConverter.UInt64BitsToDouble(0x37A00000_00000000)), // PosEpsilon = 9.1835E-41 + (UInt16BitsToBFloat16(0), 0d), // 0 + (UInt16BitsToBFloat16(0b1_00000000_0000000), -0d), // -0 + (UInt16BitsToBFloat16(0b0_10000000_1001001), 3.140625d), // 3.140625 + (UInt16BitsToBFloat16(0b1_10000000_1001001), -3.140625d), // -3.140625 + (UInt16BitsToBFloat16(0b0_10000000_0101110), 2.71875d), // 2.71875 + (UInt16BitsToBFloat16(0b1_10000000_0101110), -2.71875d), // -2.71875 + (UInt16BitsToBFloat16(0b0_01111111_1000000), 1.5d), // 1.5 + (UInt16BitsToBFloat16(0b1_01111111_1000000), -1.5d), // -1.5 + (UInt16BitsToBFloat16(0b0_01111111_1000001), 1.5078125d), // 1.5078125 + (UInt16BitsToBFloat16(0b1_01111111_1000001), -1.5078125d), // -1.5078125 + (UInt16BitsToBFloat16(0b0_00000001_0000000), BitConverter.UInt64BitsToDouble(0x3810000000000000)), // smallest normal + (UInt16BitsToBFloat16(0b0_00000000_1111111), BitConverter.UInt64BitsToDouble(0x380FC00000000000)), // largest subnormal + (UInt16BitsToBFloat16(0b0_00000000_1000000), BitConverter.UInt64BitsToDouble(0x3800000000000000)), // middle subnormal + (UInt16BitsToBFloat16(0b0_00000000_0111111), BitConverter.UInt64BitsToDouble(0x37FF800000000000)), // just below middle subnormal + (UInt16BitsToBFloat16(0b0_00000000_0000001), BitConverter.UInt64BitsToDouble(0x37A0000000000000)), // smallest subnormal + (UInt16BitsToBFloat16(0b1_00000000_0000001), BitConverter.UInt64BitsToDouble(0xB7A0000000000000)), // highest negative subnormal + (UInt16BitsToBFloat16(0b1_00000000_0111111), BitConverter.UInt64BitsToDouble(0xB7FF800000000000)), // just above negative middle subnormal + (UInt16BitsToBFloat16(0b1_00000000_1000000), BitConverter.UInt64BitsToDouble(0xB800000000000000)), // negative middle subnormal + (UInt16BitsToBFloat16(0b1_00000000_1111111), BitConverter.UInt64BitsToDouble(0xB80FC00000000000)), // lowest negative subnormal + (UInt16BitsToBFloat16(0b1_00000001_0000000), BitConverter.UInt64BitsToDouble(0xB810000000000000)) // highest negative normal + }; + + foreach ((BFloat16 original, double expected) in data) + { + yield return new object[] { original, expected }; + } + } + + [MemberData(nameof(ExplicitConversion_ToDouble_TestData))] + [Theory] + public static void ExplicitConversion_ToDouble(BFloat16 value, double expected) // Check the underlying bits for verifying NaNs + { + double d = (double)value; + AssertExtensions.Equal(expected, d); + } + + public static IEnumerable ExplicitConversion_FromSingle_TestData() + { + (float, BFloat16)[] data = + { + (MathF.PI, UInt16BitsToBFloat16(0b0_10000000_1001001)), // 3.140625 + (MathF.E, UInt16BitsToBFloat16(0b0_10000000_0101110)), // 2.71875 + (-MathF.PI, UInt16BitsToBFloat16(0b1_10000000_1001001)), // -3.140625 + (-MathF.E, UInt16BitsToBFloat16(0b1_10000000_0101110)), // -2.71875 + (float.MaxValue, UInt16BitsToBFloat16(0b0_11111111_0000000)), // Overflow + (float.MinValue, UInt16BitsToBFloat16(0b1_11111111_0000000)), // Overflow + (float.PositiveInfinity, BFloat16.PositiveInfinity), // Overflow + (float.NegativeInfinity, BFloat16.NegativeInfinity), // Overflow + (float.NaN, BFloat16.NaN), // Quiet Negative NaN + (BitConverter.UInt32BitsToSingle(0x7FC00000), UInt16BitsToBFloat16(0b0_11111111_1000000)), // Quiet Positive NaN + (BitConverter.UInt32BitsToSingle(0xFFD55555), UInt16BitsToBFloat16(0b1_11111111_1010101)), // Signalling Negative NaN + (BitConverter.UInt32BitsToSingle(0x7FD55555), UInt16BitsToBFloat16(0b0_11111111_1010101)), // Signalling Positive NaN + (float.Epsilon, UInt16BitsToBFloat16(0)), // Underflow + (-float.Epsilon, UInt16BitsToBFloat16(0b1_00000000_0000000)), // Underflow + (1f, UInt16BitsToBFloat16(0b0_01111111_0000000)), // 1 + (-1f, UInt16BitsToBFloat16(0b1_01111111_0000000)), // -1 + (0f, UInt16BitsToBFloat16(0)), // 0 + (-0f, UInt16BitsToBFloat16(0b1_00000000_0000000)), // -0 + (42f, UInt16BitsToBFloat16(0b0_10000100_0101000)), // 42 + (-42f, UInt16BitsToBFloat16(0b1_10000100_0101000)), // -42 + (0.1f, UInt16BitsToBFloat16(0b0_01111011_1001101)), // 0.10009765625 + (-0.1f, UInt16BitsToBFloat16(0b1_01111011_1001101)), // -0.10009765625 + (1.5f, UInt16BitsToBFloat16(0b0_01111111_1000000)), // 1.5 + (-1.5f, UInt16BitsToBFloat16(0b1_01111111_1000000)), // -1.5 + (1.5078125f, UInt16BitsToBFloat16(0b0_01111111_1000001)), // 1.5078125 + (-1.5078125f, UInt16BitsToBFloat16(0b1_01111111_1000001)), // -1.5078125 + (BitConverter.UInt32BitsToSingle(0x00800000), UInt16BitsToBFloat16(0b0_00000001_0000000)), // smallest normal + (BitConverter.UInt32BitsToSingle(0x007F0000), UInt16BitsToBFloat16(0b0_00000000_1111111)), // largest subnormal + (BitConverter.UInt32BitsToSingle(0x00400000), UInt16BitsToBFloat16(0b0_00000000_1000000)), // middle subnormal + (BitConverter.UInt32BitsToSingle(0x003F0000), UInt16BitsToBFloat16(0b0_00000000_0111111)), // just below middle subnormal + (BitConverter.UInt32BitsToSingle(0x00010000), UInt16BitsToBFloat16(0b0_00000000_0000001)), // smallest subnormal + (BitConverter.UInt32BitsToSingle(0x80010000), UInt16BitsToBFloat16(0b1_00000000_0000001)), // highest negative subnormal + (BitConverter.UInt32BitsToSingle(0x803F0000), UInt16BitsToBFloat16(0b1_00000000_0111111)), // just above negative middle subnormal + (BitConverter.UInt32BitsToSingle(0x80400000), UInt16BitsToBFloat16(0b1_00000000_1000000)), // negative middle subnormal + (BitConverter.UInt32BitsToSingle(0x807F0000), UInt16BitsToBFloat16(0b1_00000000_1111111)), // lowest negative subnormal + (BitConverter.UInt32BitsToSingle(0x80800000), UInt16BitsToBFloat16(0b1_00000001_0000000)), // highest negative normal + (BitConverter.UInt32BitsToSingle(0b0_10001001_00000111000000000000001), + UInt16BitsToBFloat16(0b0_10001001_0000100)), // 1052+ULP rounds up + (BitConverter.UInt32BitsToSingle(0b0_10001001_00000111000000000000000), + UInt16BitsToBFloat16(0b0_10001001_0000100)), // 1052 rounds to even + (BitConverter.UInt32BitsToSingle(0b0_10001001_00000110111111111111111), + UInt16BitsToBFloat16(0b0_10001001_0000011)), // 1052-ULP rounds down + (BitConverter.UInt32BitsToSingle(0b0_10001001_00000101000000000000000), + UInt16BitsToBFloat16(0b0_10001001_0000010)), // 1044 rounds to even + (BitConverter.UInt32BitsToSingle(0b1_10001001_00000110111111111111111), + UInt16BitsToBFloat16(0b1_10001001_0000011)), // -1052+ULP rounds towards zero + (BitConverter.UInt32BitsToSingle(0b1_10001001_00000111000000000000000), + UInt16BitsToBFloat16(0b1_10001001_0000100)), // -1052 rounds to even + (BitConverter.UInt32BitsToSingle(0b1_10001001_00000111000000000000001), + UInt16BitsToBFloat16(0b1_10001001_0000100)), // -1052-ULP rounds away from zero + (BitConverter.UInt32BitsToSingle(0b1_10001001_00000101000000000000000), + UInt16BitsToBFloat16(0b1_10001001_0000010)), // -1044 rounds to even + (BitConverter.UInt32BitsToSingle(0b0_00000000_10000111000000000000001), + UInt16BitsToBFloat16(0b0_00000000_1000100)), // subnormal + ULP rounds up + (BitConverter.UInt32BitsToSingle(0b0_00000000_10000111000000000000000), + UInt16BitsToBFloat16(0b0_00000000_1000100)), // subnormal rounds to even + (BitConverter.UInt32BitsToSingle(0b0_00000000_10000110111111111111111), + UInt16BitsToBFloat16(0b0_00000000_1000011)), // subnormal - ULP rounds down + (BitConverter.UInt32BitsToSingle(0b1_00000000_10000110111111111111111), + UInt16BitsToBFloat16(0b1_00000000_1000011)), // neg subnormal + ULP rounds higher + (BitConverter.UInt32BitsToSingle(0b1_00000000_10000111000000000000000), + UInt16BitsToBFloat16(0b1_00000000_1000100)), // neg subnormal rounds to even + (BitConverter.UInt32BitsToSingle(0b1_00000000_10000111000000000000001), + UInt16BitsToBFloat16(0b1_00000000_1000100)), // neg subnormal - ULP rounds lower, + (BitConverter.UInt32BitsToSingle(0b0_00000000_00000000110000000000000), + UInt16BitsToBFloat16(0b0_00000_000000000)), // (BFloat16 minimum subnormal / 2) should underflow to zero + }; + + foreach ((float original, BFloat16 expected) in data) + { + yield return new object[] { original, expected }; + } + } + + [MemberData(nameof(ExplicitConversion_FromSingle_TestData))] + [Theory] + public static void ExplicitConversion_FromSingle(float f, BFloat16 expected) // Check the underlying bits for verifying NaNs + { + BFloat16 b16 = (BFloat16)f; + AssertEqual(expected, b16); + } + + public static IEnumerable ExplicitConversion_FromDouble_TestData() + { + (double, BFloat16)[] data = + { + (Math.PI, UInt16BitsToBFloat16(0b0_10000000_1001001)), // 3.140625 + (Math.E, UInt16BitsToBFloat16(0b0_10000000_0101110)), // 2.71875 + (-Math.PI, UInt16BitsToBFloat16(0b1_10000000_1001001)), // -3.140625 + (-Math.E, UInt16BitsToBFloat16(0b1_10000000_0101110)), // -2.71875 + (double.MaxValue, BFloat16.PositiveInfinity), // Overflow + (double.MinValue, BFloat16.NegativeInfinity), // Overflow + (double.PositiveInfinity, BFloat16.PositiveInfinity), // Overflow + (double.NegativeInfinity, BFloat16.NegativeInfinity), // Overflow + (double.NaN, BFloat16.NaN), // Quiet Negative NaN + (BitConverter.UInt64BitsToDouble(0x7FF80000_00000000), UInt16BitsToBFloat16(0b0_11111111_1000000)), // Quiet Positive NaN + (BitConverter.UInt64BitsToDouble(0xFFFAAAAA_AAAAAAAA), UInt16BitsToBFloat16(0b1_11111111_1010101)), // Signalling Negative NaN + (BitConverter.UInt64BitsToDouble(0x7FFAAAAA_AAAAAAAA), UInt16BitsToBFloat16(0b0_11111111_1010101)), // Signalling Positive NaN + (double.Epsilon, UInt16BitsToBFloat16(0)), // Underflow + (-double.Epsilon, UInt16BitsToBFloat16(0b1_00000000_0000000)), // Underflow (1f, UInt16BitsToBFloat16(0b0_01111111_0000000)), // 1 + (-1d, UInt16BitsToBFloat16(0b1_01111111_0000000)), // -1 + (0d, UInt16BitsToBFloat16(0)), // 0 + (-0d, UInt16BitsToBFloat16(0b1_00000000_0000000)), // -0 + (42d, UInt16BitsToBFloat16(0b0_10000100_0101000)), // 42 + (-42d, UInt16BitsToBFloat16(0b1_10000100_0101000)), // -42 + (0.1d, UInt16BitsToBFloat16(0b0_01111011_1001101)), // 0.10009765625 + (-0.1d, UInt16BitsToBFloat16(0b1_01111011_1001101)), // -0.10009765625 + (1.5d, UInt16BitsToBFloat16(0b0_01111111_1000000)), // 1.5 + (-1.5d, UInt16BitsToBFloat16(0b1_01111111_1000000)), // -1.5 + (1.5078125d, UInt16BitsToBFloat16(0b0_01111111_1000001)), // 1.5078125 + (-1.5078125d, UInt16BitsToBFloat16(0b1_01111111_1000001)), // -1.5078125 + (BitConverter.UInt64BitsToDouble(0x3810000000000000), UInt16BitsToBFloat16(0b0_00000001_0000000)), // smallest normal + (BitConverter.UInt64BitsToDouble(0x380FC00000000000), UInt16BitsToBFloat16(0b0_00000000_1111111)), // largest subnormal + (BitConverter.UInt64BitsToDouble(0x3800000000000000), UInt16BitsToBFloat16(0b0_00000000_1000000)), // middle subnormal + (BitConverter.UInt64BitsToDouble(0x37FF800000000000), UInt16BitsToBFloat16(0b0_00000000_0111111)), // just below middle subnormal + (BitConverter.UInt64BitsToDouble(0x37A0000000000000), UInt16BitsToBFloat16(0b0_00000000_0000001)), // smallest subnormal + (BitConverter.UInt64BitsToDouble(0xB7A0000000000000), UInt16BitsToBFloat16(0b1_00000000_0000001)), // highest negative subnormal + (BitConverter.UInt64BitsToDouble(0xB7FF800000000000), UInt16BitsToBFloat16(0b1_00000000_0111111)), // just above negative middle subnormal + (BitConverter.UInt64BitsToDouble(0xB800000000000000), UInt16BitsToBFloat16(0b1_00000000_1000000)), // negative middle subnormal + (BitConverter.UInt64BitsToDouble(0xB80FC00000000000), UInt16BitsToBFloat16(0b1_00000000_1111111)), // lowest negative subnormal + (BitConverter.UInt64BitsToDouble(0xB810000000000000), UInt16BitsToBFloat16(0b1_00000001_0000000)), // highest negative normal + (BitConverter.UInt64BitsToDouble(0x4090700000000001), + UInt16BitsToBFloat16(0b0_10001001_0000100)), // 1052+ULP rounds up + (BitConverter.UInt64BitsToDouble(0x4090700000000000), + UInt16BitsToBFloat16(0b0_10001001_0000100)), // 1052 rounds to even + (BitConverter.UInt64BitsToDouble(0x40906FFFFFFFFFFF), + UInt16BitsToBFloat16(0b0_10001001_0000011)), // 1052-ULP rounds down + (BitConverter.UInt64BitsToDouble(0x4090500000000000), + UInt16BitsToBFloat16(0b0_10001001_0000010)), // 1044 rounds to even + (BitConverter.UInt64BitsToDouble(0xC0906FFFFFFFFFFF), + UInt16BitsToBFloat16(0b1_10001001_0000011)), // -1052+ULP rounds towards zero + (BitConverter.UInt64BitsToDouble(0xC090700000000000), + UInt16BitsToBFloat16(0b1_10001001_0000100)), // -1052 rounds to even + (BitConverter.UInt64BitsToDouble(0xC090700000000001), + UInt16BitsToBFloat16(0b1_10001001_0000100)), // -1052-ULP rounds away from zero + (BitConverter.UInt64BitsToDouble(0xC090500000000000), + UInt16BitsToBFloat16(0b1_10001001_0000010)), // -1044 rounds to even + (BitConverter.UInt64BitsToDouble(0x3800E00000000001), + UInt16BitsToBFloat16(0b0_00000000_1000100)), // subnormal + ULP rounds up + (BitConverter.UInt64BitsToDouble(0x3800E00000000000), + UInt16BitsToBFloat16(0b0_00000000_1000100)), // subnormal rounds to even + (BitConverter.UInt64BitsToDouble(0x3800DFFFFFFFFFFF), + UInt16BitsToBFloat16(0b0_00000000_1000011)), // subnormal - ULP rounds down + (BitConverter.UInt64BitsToDouble(0xB800DFFFFFFFFFFF), + UInt16BitsToBFloat16(0b1_00000000_1000011)), // neg subnormal + ULP rounds higher + (BitConverter.UInt64BitsToDouble(0xB800E00000000000), + UInt16BitsToBFloat16(0b1_00000000_1000100)), // neg subnormal rounds to even + (BitConverter.UInt64BitsToDouble(0xB800E00000000001), + UInt16BitsToBFloat16(0b1_00000000_1000100)), // neg subnormal - ULP rounds lower + (BitConverter.UInt64BitsToDouble(0x3788000000000000), UInt16BitsToBFloat16(0b0_00000_000000000)), // (BFloat16 minimum subnormal / 2) should underflow to zero + }; + + foreach ((double original, BFloat16 expected) in data) + { + yield return new object[] { original, expected }; + } + } + + [MemberData(nameof(ExplicitConversion_FromDouble_TestData))] + [Theory] + public static void ExplicitConversion_FromDouble(double d, BFloat16 expected) // Check the underlying bits for verifying NaNs + { + BFloat16 b16 = (BFloat16)d; + AssertEqual(expected, b16); + } + + public static IEnumerable Parse_Valid_TestData() + { + NumberStyles defaultStyle = NumberStyles.Float | NumberStyles.AllowThousands; + + NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; + + var dollarSignCommaSeparatorFormat = new NumberFormatInfo() + { + CurrencySymbol = "$", + CurrencyGroupSeparator = "," + }; + + var decimalSeparatorFormat = new NumberFormatInfo() + { + NumberDecimalSeparator = "." + }; + + NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo; + + yield return new object[] { "-123", defaultStyle, null, -123.0f }; + yield return new object[] { "0", defaultStyle, null, 0.0f }; + yield return new object[] { "123", defaultStyle, null, 123.0f }; + yield return new object[] { " 123 ", defaultStyle, null, 123.0f }; + yield return new object[] { (567.89f).ToString(), defaultStyle, null, 567.89f }; + yield return new object[] { (-567.89f).ToString(), defaultStyle, null, -567.89f }; + yield return new object[] { "1E23", defaultStyle, null, 1E23f }; + + yield return new object[] { emptyFormat.NumberDecimalSeparator + "234", defaultStyle, null, 0.234f }; + yield return new object[] { "234" + emptyFormat.NumberDecimalSeparator, defaultStyle, null, 234.0f }; + yield return new object[] { new string('0', 13) + "338953138925153547590470800371487866880" + emptyFormat.NumberDecimalSeparator, defaultStyle, null, 3.3895314e38f }; + yield return new object[] { new string('0', 14) + "338953138925153547590470800371487866880" + emptyFormat.NumberDecimalSeparator, defaultStyle, null, 3.3895314e38f }; + + // 2^11 + 1. Not exactly representable + yield return new object[] { "2049.0", defaultStyle, invariantFormat, 2048.0f }; + yield return new object[] { "2049.000000000000001", defaultStyle, invariantFormat, 2050.0f }; + yield return new object[] { "2049.0000000000000001", defaultStyle, invariantFormat, 2050.0f }; + yield return new object[] { "2049.00000000000000001", defaultStyle, invariantFormat, 2050.0f }; + yield return new object[] { "5.000000000000000004", defaultStyle, invariantFormat, 5.0f }; + yield return new object[] { "5.0000000000000000004", defaultStyle, invariantFormat, 5.0f }; + yield return new object[] { "5.004", defaultStyle, invariantFormat, 5.004f }; + yield return new object[] { "5.004000000000000000", defaultStyle, invariantFormat, 5.004f }; + yield return new object[] { "5.0040000000000000000", defaultStyle, invariantFormat, 5.004f }; + yield return new object[] { "5.040", defaultStyle, invariantFormat, 5.04f }; + + yield return new object[] { "5004.000000000000000", defaultStyle, invariantFormat, 5004.0f }; + yield return new object[] { "50040.0", defaultStyle, invariantFormat, 50040.0f }; + yield return new object[] { "5004", defaultStyle, invariantFormat, 5004.0f }; + yield return new object[] { "050040", defaultStyle, invariantFormat, 50040.0f }; + yield return new object[] { "0.000000000000000000", defaultStyle, invariantFormat, 0.0f }; + yield return new object[] { "0.005", defaultStyle, invariantFormat, 0.005f }; + yield return new object[] { "0.0400", defaultStyle, invariantFormat, 0.04f }; + yield return new object[] { "1200e0", defaultStyle, invariantFormat, 1200.0f }; + yield return new object[] { "120100e-4", defaultStyle, invariantFormat, 12.01f }; + yield return new object[] { "12010.00e-4", defaultStyle, invariantFormat, 1.201f }; + yield return new object[] { "12000e-4", defaultStyle, invariantFormat, 1.2f }; + yield return new object[] { "1200", defaultStyle, invariantFormat, 1200.0f }; + + yield return new object[] { (123.1f).ToString(), NumberStyles.AllowDecimalPoint, null, 123.1f }; + yield return new object[] { (1000.0f).ToString("N0"), NumberStyles.AllowThousands, null, 1000.0f }; + + yield return new object[] { "123", NumberStyles.Any, emptyFormat, 123.0f }; + yield return new object[] { (123.567f).ToString(), NumberStyles.Any, emptyFormat, 123.567f }; + yield return new object[] { "123", NumberStyles.Float, emptyFormat, 123.0f }; + yield return new object[] { "$1,000", NumberStyles.Currency, dollarSignCommaSeparatorFormat, 1000.0f }; + yield return new object[] { "$1000", NumberStyles.Currency, dollarSignCommaSeparatorFormat, 1000.0f }; + yield return new object[] { "123.123", NumberStyles.Float, decimalSeparatorFormat, 123.123f }; + yield return new object[] { "(123)", NumberStyles.AllowParentheses, decimalSeparatorFormat, -123.0f }; + + yield return new object[] { "NaN", NumberStyles.Any, invariantFormat, float.NaN }; + yield return new object[] { "Infinity", NumberStyles.Any, invariantFormat, float.PositiveInfinity }; + yield return new object[] { "-Infinity", NumberStyles.Any, invariantFormat, float.NegativeInfinity }; + } + + [Theory] + [MemberData(nameof(Parse_Valid_TestData))] + public static void Parse(string value, NumberStyles style, IFormatProvider provider, float expectedFloat) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + BFloat16 result; + BFloat16 expected = (BFloat16)expectedFloat; + if ((style & ~(NumberStyles.Float | NumberStyles.AllowThousands)) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(BFloat16.TryParse(value, out result)); + Assert.True(expected.Equals(result)); + + Assert.Equal(expected, BFloat16.Parse(value)); + } + + Assert.True(expected.Equals(BFloat16.Parse(value, provider: provider))); + } + + // Use Parse(string, NumberStyles, IFormatProvider) + Assert.True(BFloat16.TryParse(value, style, provider, out result)); + Assert.True(expected.Equals(result) || (BFloat16.IsNaN(expected) && BFloat16.IsNaN(result))); + + Assert.True(expected.Equals(BFloat16.Parse(value, style, provider)) || (BFloat16.IsNaN(expected) && BFloat16.IsNaN(result))); + + if (isDefaultProvider) + { + // Use Parse(string, NumberStyles) or Parse(string, NumberStyles, IFormatProvider) + Assert.True(BFloat16.TryParse(value, style, NumberFormatInfo.CurrentInfo, out result)); + Assert.True(expected.Equals(result)); + + Assert.True(expected.Equals(BFloat16.Parse(value, style))); + Assert.True(expected.Equals(BFloat16.Parse(value, style, NumberFormatInfo.CurrentInfo))); + } + } + + public static IEnumerable Parse_Invalid_TestData() + { + NumberStyles defaultStyle = NumberStyles.Float; + + var dollarSignDecimalSeparatorFormat = new NumberFormatInfo(); + dollarSignDecimalSeparatorFormat.CurrencySymbol = "$"; + dollarSignDecimalSeparatorFormat.NumberDecimalSeparator = "."; + + yield return new object[] { null, defaultStyle, null, typeof(ArgumentNullException) }; + yield return new object[] { "", defaultStyle, null, typeof(FormatException) }; + yield return new object[] { " ", defaultStyle, null, typeof(FormatException) }; + yield return new object[] { "Garbage", defaultStyle, null, typeof(FormatException) }; + + yield return new object[] { "ab", defaultStyle, null, typeof(FormatException) }; // Hex value + yield return new object[] { "(123)", defaultStyle, null, typeof(FormatException) }; // Parentheses + yield return new object[] { (100.0f).ToString("C0"), defaultStyle, null, typeof(FormatException) }; // Currency + + yield return new object[] { (123.456f).ToString(), NumberStyles.Integer, null, typeof(FormatException) }; // Decimal + yield return new object[] { " " + (123.456f).ToString(), NumberStyles.None, null, typeof(FormatException) }; // Leading space + yield return new object[] { (123.456f).ToString() + " ", NumberStyles.None, null, typeof(FormatException) }; // Leading space + yield return new object[] { "1E23", NumberStyles.None, null, typeof(FormatException) }; // Exponent + + yield return new object[] { "ab", NumberStyles.None, null, typeof(FormatException) }; // Negative hex value + yield return new object[] { " 123 ", NumberStyles.None, null, typeof(FormatException) }; // Trailing and leading whitespace + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + BFloat16 result; + if ((style & ~(NumberStyles.Float | NumberStyles.AllowThousands)) == 0 && style != NumberStyles.None && (style & NumberStyles.AllowLeadingWhite) == (style & NumberStyles.AllowTrailingWhite)) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.False(BFloat16.TryParse(value, out result)); + Assert.Equal(default(BFloat16), result); + + Assert.Throws(exceptionType, () => BFloat16.Parse(value)); + } + + Assert.Throws(exceptionType, () => BFloat16.Parse(value, provider: provider)); + } + + // Use Parse(string, NumberStyles, IFormatProvider) + Assert.False(BFloat16.TryParse(value, style, provider, out result)); + Assert.Equal(default(BFloat16), result); + + Assert.Throws(exceptionType, () => BFloat16.Parse(value, style, provider)); + + if (isDefaultProvider) + { + // Use Parse(string, NumberStyles) or Parse(string, NumberStyles, IFormatProvider) + Assert.False(BFloat16.TryParse(value, style, NumberFormatInfo.CurrentInfo, out result)); + Assert.Equal(default(BFloat16), result); + + Assert.Throws(exceptionType, () => BFloat16.Parse(value, style)); + Assert.Throws(exceptionType, () => BFloat16.Parse(value, style, NumberFormatInfo.CurrentInfo)); + } + } + + public static IEnumerable Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; + } + + const NumberStyles DefaultStyle = NumberStyles.Float | NumberStyles.AllowThousands; + + yield return new object[] { "-123", 1, 3, DefaultStyle, null, (float)123 }; + yield return new object[] { "-123", 0, 3, DefaultStyle, null, (float)-12 }; + yield return new object[] { "1E23", 0, 3, DefaultStyle, null, (float)1E2 }; + yield return new object[] { "123", 0, 2, NumberStyles.Float, new NumberFormatInfo(), (float)12 }; + yield return new object[] { "$1,000", 1, 3, NumberStyles.Currency, new NumberFormatInfo() { CurrencySymbol = "$", CurrencyGroupSeparator = "," }, (float)10 }; + yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, (float)123 }; + yield return new object[] { "-Infinity", 1, 8, NumberStyles.Any, NumberFormatInfo.InvariantInfo, float.PositiveInfinity }; + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, float expectedFloat) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + BFloat16 result; + BFloat16 expected = (BFloat16)expectedFloat; + if ((style & ~(NumberStyles.Float | NumberStyles.AllowThousands)) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(BFloat16.TryParse(value.AsSpan(offset, count), out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, BFloat16.Parse(value.AsSpan(offset, count))); + } + + Assert.Equal(expected, BFloat16.Parse(value.AsSpan(offset, count), provider: provider)); + } + + Assert.True(expected.Equals(BFloat16.Parse(value.AsSpan(offset, count), style, provider)) || (BFloat16.IsNaN(expected) && BFloat16.IsNaN(BFloat16.Parse(value.AsSpan(offset, count), style, provider)))); + + Assert.True(BFloat16.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.True(expected.Equals(result) || (BFloat16.IsNaN(expected) && BFloat16.IsNaN(result))); + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value != null) + { + Assert.Throws(exceptionType, () => float.Parse(value.AsSpan(), style, provider)); + + Assert.False(float.TryParse(value.AsSpan(), style, provider, out float result)); + Assert.Equal(0, result); + } + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Utf8Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, float expectedFloat) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + + BFloat16 result; + BFloat16 expected = (BFloat16)expectedFloat; + + ReadOnlySpan valueUtf8 = Encoding.UTF8.GetBytes(value, offset, count); + + if ((style & ~(NumberStyles.Float | NumberStyles.AllowThousands)) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(BFloat16.TryParse(valueUtf8, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, BFloat16.Parse(valueUtf8)); + } + + Assert.Equal(expected, BFloat16.Parse(valueUtf8, provider: provider)); + } + + Assert.True(expected.Equals(BFloat16.Parse(valueUtf8, style, provider)) || (BFloat16.IsNaN(expected) && BFloat16.IsNaN(BFloat16.Parse(value.AsSpan(offset, count), style, provider)))); + + Assert.True(BFloat16.TryParse(valueUtf8, style, provider, out result)); + Assert.True(expected.Equals(result) || (BFloat16.IsNaN(expected) && BFloat16.IsNaN(result))); + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Utf8Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value != null) + { + ReadOnlySpan valueUtf8 = Encoding.UTF8.GetBytes(value); + Exception e = Assert.Throws(exceptionType, () => BFloat16.Parse(Encoding.UTF8.GetBytes(value), style, provider)); + if (e is FormatException fe) + { + Assert.Contains(value, fe.Message); + } + + Assert.False(float.TryParse(valueUtf8, style, provider, out float result)); + Assert.Equal(0, result); + } + } + + [Fact] + public static void Parse_Utf8Span_InvalidUtf8() + { + FormatException fe = Assert.Throws(() => BFloat16.Parse([0xA0])); + Assert.DoesNotContain("A0", fe.Message, StringComparison.Ordinal); + Assert.DoesNotContain("ReadOnlySpan", fe.Message, StringComparison.Ordinal); + Assert.DoesNotContain("\uFFFD", fe.Message, StringComparison.Ordinal); + } + + public static IEnumerable ToString_TestData() + { + yield return new object[] { -4580.0f, "G", null, "-4580" }; + yield return new object[] { 0.0f, "G", null, "0" }; + yield return new object[] { 4580.0f, "G", null, "4580" }; + + yield return new object[] { float.NaN, "G", null, "NaN" }; + + yield return new object[] { 2464.0f, "N", null, "2,464.00" }; + + // Changing the negative pattern doesn't do anything without also passing in a format string + var customNegativePattern = new NumberFormatInfo() { NumberNegativePattern = 0 }; + yield return new object[] { -6300.0f, "G", customNegativePattern, "-6300" }; + + var customNegativeSignDecimalGroupSeparator = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*" + }; + yield return new object[] { -2464.0f, "N", customNegativeSignDecimalGroupSeparator, "#2*464~00" }; + yield return new object[] { 2464.0f, "N", customNegativeSignDecimalGroupSeparator, "2*464~00" }; + + var customNegativeSignGroupSeparatorNegativePattern = new NumberFormatInfo() + { + NegativeSign = "xx", // Set to trash to make sure it doesn't show up + NumberGroupSeparator = "*", + NumberNegativePattern = 0 + }; + yield return new object[] { -2464.0f, "N", customNegativeSignGroupSeparatorNegativePattern, "(2*464.00)" }; + + NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo; + yield return new object[] { float.NaN, "G", invariantFormat, "NaN" }; + yield return new object[] { float.PositiveInfinity, "G", invariantFormat, "Infinity" }; + yield return new object[] { float.NegativeInfinity, "G", invariantFormat, "-Infinity" }; + } + + public static IEnumerable ToString_TestData_NotNetFramework() + { + foreach (var testData in ToString_TestData()) + { + yield return testData; + } + + yield return new object[] { BFloat16.MinValue, "G", null, "-3.39E+38" }; + yield return new object[] { BFloat16.MaxValue, "G", null, "3.39E+38" }; + + yield return new object[] { BFloat16.Epsilon, "G", null, "1E-40" }; + + NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo; + yield return new object[] { BFloat16.Epsilon, "G", invariantFormat, "1E-40" }; + + yield return new object[] { 32.5f, "C100", invariantFormat, "\u00A432.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }; + yield return new object[] { 32.5f, "P100", invariantFormat, "3,250.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %" }; + yield return new object[] { 32.5f, "E100", invariantFormat, "3.2500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+001" }; + yield return new object[] { 32.5f, "F100", invariantFormat, "32.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }; + yield return new object[] { 32.5f, "N100", invariantFormat, "32.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }; + } + + [Fact] + public static void Test_ToString_NotNetFramework() + { + using (new ThreadCultureChange(CultureInfo.InvariantCulture)) + { + foreach (object[] testdata in ToString_TestData_NotNetFramework()) + { + ToStringTest(testdata[0] is float floatData ? (BFloat16)floatData : (BFloat16)testdata[0], (string)testdata[1], (IFormatProvider)testdata[2], (string)testdata[3]); + } + } + } + + private static void ToStringTest(BFloat16 f, string format, IFormatProvider provider, string expected) + { + bool isDefaultProvider = provider == null; + if (string.IsNullOrEmpty(format) || format.ToUpperInvariant() == "G") + { + if (isDefaultProvider) + { + Assert.Equal(expected, f.ToString()); + Assert.Equal(expected, f.ToString((IFormatProvider)null)); + } + Assert.Equal(expected, f.ToString(provider)); + } + if (isDefaultProvider) + { + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant())); // If format is upper case, then exponents are printed in upper case + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant())); // If format is lower case, then exponents are printed in lower case + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant(), null)); + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant(), null)); + } + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant(), provider)); + } + + [Fact] + public static void ToString_InvalidFormat_ThrowsFormatException() + { + BFloat16 f = (BFloat16)123.0f; + Assert.Throws(() => f.ToString("Y")); // Invalid format + Assert.Throws(() => f.ToString("Y", null)); // Invalid format + long intMaxPlus1 = (long)int.MaxValue + 1; + string intMaxPlus1String = intMaxPlus1.ToString(); + Assert.Throws(() => f.ToString("E" + intMaxPlus1String)); + } + + [Fact] + public static void TryFormat() + { + using (new ThreadCultureChange(CultureInfo.InvariantCulture)) + { + foreach (object[] testdata in ToString_TestData()) + { + float localI = (float)testdata[0]; + string localFormat = (string)testdata[1]; + IFormatProvider localProvider = (IFormatProvider)testdata[2]; + string localExpected = (string)testdata[3]; + + try + { + NumberFormatTestHelper.TryFormatNumberTest(localI, localFormat, localProvider, localExpected, formatCasingMatchesOutput: false); + } + catch (Exception exc) + { + throw new Exception($"Failed on `{localI}`, `{localFormat}`, `{localProvider}`, `{localExpected}`. {exc}"); + } + } + } + } + + public static IEnumerable ToStringRoundtrip_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.MinValue }; + yield return new object[] { -MathF.PI }; + yield return new object[] { -MathF.E }; + yield return new object[] { -0.845512408f }; + yield return new object[] { -0.0f }; + yield return new object[] { BFloat16.NaN }; + yield return new object[] { 0.0f }; + yield return new object[] { 0.845512408f }; + yield return new object[] { BFloat16.Epsilon }; + yield return new object[] { MathF.E }; + yield return new object[] { MathF.PI }; + yield return new object[] { BFloat16.MaxValue }; + yield return new object[] { BFloat16.PositiveInfinity }; + + yield return new object[] { (UInt16BitsToBFloat16(0b0_00000001_0000000)) }; // smallest normal + yield return new object[] { (UInt16BitsToBFloat16(0b0_00000000_1111111)) }; // largest subnormal + yield return new object[] { (UInt16BitsToBFloat16(0b0_00000000_1000000)) }; // middle subnormal + yield return new object[] { (UInt16BitsToBFloat16(0b0_00000000_0111111)) }; // just below middle subnormal + yield return new object[] { (UInt16BitsToBFloat16(0b0_00000000_0000000)) }; // smallest subnormal + yield return new object[] { (UInt16BitsToBFloat16(0b1_00000000_0000000)) }; // highest negative subnormal + yield return new object[] { (UInt16BitsToBFloat16(0b1_00000000_0111111)) }; // just above negative middle subnormal + yield return new object[] { (UInt16BitsToBFloat16(0b1_00000000_1000000)) }; // negative middle subnormal + yield return new object[] { (UInt16BitsToBFloat16(0b1_00000000_1111111)) }; // lowest negative subnormal + yield return new object[] { (UInt16BitsToBFloat16(0b1_00000001_0000000)) }; // highest negative normal + } + + [Theory] + [MemberData(nameof(ToStringRoundtrip_TestData))] + public static void ToStringRoundtrip(object o_value) + { + float value = o_value is float floatValue ? floatValue : (float)(BFloat16)o_value; + BFloat16 result = BFloat16.Parse(value.ToString()); + AssertEqual((BFloat16)value, result); + } + + [Theory] + [MemberData(nameof(ToStringRoundtrip_TestData))] + public static void ToStringRoundtrip_R(object o_value) + { + float value = o_value is float floatValue ? floatValue : (float)(BFloat16)o_value; + BFloat16 result = BFloat16.Parse(value.ToString("R")); + AssertEqual((BFloat16)value, result); + } + + public static IEnumerable RoundTripFloat_CornerCases() + { + // Magnitude smaller than 2^-133 maps to 0 + yield return new object[] { (BFloat16)(4.6e-41f), 0 }; + yield return new object[] { (BFloat16)(-4.6e-41f), 0 }; + // Magnitude smaller than 2^(map to subnormals + yield return new object[] { (BFloat16)(0.567e-39f), 0.567e-39f }; + yield return new object[] { (BFloat16)(-0.567e-39f), -0.567e-39f }; + // Normal numbers + yield return new object[] { (BFloat16)(55.77f), 55.75f }; + yield return new object[] { (BFloat16)(-55.77f), -55.75f }; + // Magnitude smaller than 2^(map to infinity + yield return new object[] { (BFloat16)(float.BitDecrement(float.PositiveInfinity)), float.PositiveInfinity }; + yield return new object[] { (BFloat16)(float.BitIncrement(float.NegativeInfinity)), float.NegativeInfinity }; + // Infinity and NaN map to infinity and Nan + yield return new object[] { BFloat16.PositiveInfinity, float.PositiveInfinity }; + yield return new object[] { BFloat16.NegativeInfinity, float.NegativeInfinity }; + yield return new object[] { BFloat16.NaN, float.NaN }; + } + + [Theory] + [MemberData(nameof(RoundTripFloat_CornerCases))] + public static void ToSingle(BFloat16 BFloat16, float verify) + { + float f = (float)BFloat16; + Assert.Equal(f, verify, precision: 1); + } + + [Fact] + public static void EqualityMethodAndOperator() + { + Assert.True(BFloat16.NaN.Equals(BFloat16.NaN)); + Assert.False(BFloat16.NaN == BFloat16.NaN); + Assert.Equal(BFloat16.NaN, BFloat16.NaN); + } + + + public static IEnumerable MaxMagnitudeNumber_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.PositiveInfinity, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NegativeInfinity, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.MinValue, BFloat16.MaxValue, BFloat16.MaxValue }; + yield return new object[] { BFloat16.MaxValue, BFloat16.MinValue, BFloat16.MaxValue }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.NaN }; + yield return new object[] { BFloat16.NaN, (BFloat16)BFloat16.One, (BFloat16)BFloat16.One }; + yield return new object[] { (BFloat16)BFloat16.One, BFloat16.NaN, (BFloat16)BFloat16.One }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NaN, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.NaN, BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.NaN, BFloat16.PositiveInfinity, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.NaN, BFloat16.NegativeInfinity, BFloat16.NegativeInfinity }; + yield return new object[] { (BFloat16)(-0.0f), (BFloat16)0.0f, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)0.0f, (BFloat16)(-0.0f), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)2.0f, (BFloat16)(-3.0f), (BFloat16)(-3.0f) }; + yield return new object[] { (BFloat16)(-3.0f), (BFloat16)2.0f, (BFloat16)(-3.0f) }; + yield return new object[] { (BFloat16)3.0f, (BFloat16)(-2.0f), (BFloat16)3.0f }; + yield return new object[] { (BFloat16)(-2.0f), (BFloat16)3.0f, (BFloat16)3.0f }; + } + + [Theory] + [MemberData(nameof(MaxMagnitudeNumber_TestData))] + public static void MaxMagnitudeNumberTest(BFloat16 x, BFloat16 y, BFloat16 expectedResult) + { + AssertEqual(expectedResult, BFloat16.MaxMagnitudeNumber(x, y), (BFloat16)0.0f); + } + + public static IEnumerable MaxNumber_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.PositiveInfinity, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NegativeInfinity, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.MinValue, BFloat16.MaxValue, BFloat16.MaxValue }; + yield return new object[] { BFloat16.MaxValue, BFloat16.MinValue, BFloat16.MaxValue }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.NaN }; + yield return new object[] { BFloat16.NaN, (BFloat16)BFloat16.One, (BFloat16)BFloat16.One }; + yield return new object[] { (BFloat16)BFloat16.One, BFloat16.NaN, (BFloat16)BFloat16.One }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NaN, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.NaN, BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.NaN, BFloat16.PositiveInfinity, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.NaN, BFloat16.NegativeInfinity, BFloat16.NegativeInfinity }; + yield return new object[] { (BFloat16)(-0.0f), (BFloat16)0.0f, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)0.0f, (BFloat16)(-0.0f), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)2.0f, (BFloat16)(-3.0f), (BFloat16)2.0f }; + yield return new object[] { (BFloat16)(-3.0f), (BFloat16)2.0f, (BFloat16)2.0f }; + yield return new object[] { (BFloat16)3.0f, (BFloat16)(-2.0f), (BFloat16)3.0f }; + yield return new object[] { (BFloat16)(-2.0f), (BFloat16)3.0f, (BFloat16)3.0f }; + } + + [Theory] + [MemberData(nameof(MaxNumber_TestData))] + public static void MaxNumberTest(BFloat16 x, BFloat16 y, BFloat16 expectedResult) + { + AssertEqual(expectedResult, BFloat16.MaxNumber(x, y), (BFloat16)0.0f); + } + + public static IEnumerable MinMagnitudeNumber_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.PositiveInfinity, BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NegativeInfinity, BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.MinValue, BFloat16.MaxValue, BFloat16.MinValue }; + yield return new object[] { BFloat16.MaxValue, BFloat16.MinValue, BFloat16.MinValue }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.NaN }; + yield return new object[] { BFloat16.NaN, (BFloat16)BFloat16.One, (BFloat16)BFloat16.One }; + yield return new object[] { (BFloat16)BFloat16.One, BFloat16.NaN, (BFloat16)BFloat16.One }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NaN, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.NaN, BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.NaN, BFloat16.PositiveInfinity, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.NaN, BFloat16.NegativeInfinity, BFloat16.NegativeInfinity }; + yield return new object[] { (BFloat16)(-0.0f), (BFloat16)0.0f, (BFloat16)(-0.0f) }; + yield return new object[] { (BFloat16)0.0f, (BFloat16)(-0.0f), (BFloat16)(-0.0f) }; + yield return new object[] { (BFloat16)2.0f, (BFloat16)(-3.0f), (BFloat16)2.0f }; + yield return new object[] { (BFloat16)(-3.0f), (BFloat16)2.0f, (BFloat16)2.0f }; + yield return new object[] { (BFloat16)3.0f, (BFloat16)(-2.0f), (BFloat16)(-2.0f) }; + yield return new object[] { (BFloat16)(-2.0f), (BFloat16)3.0f, (BFloat16)(-2.0f) }; + } + + [Theory] + [MemberData(nameof(MinMagnitudeNumber_TestData))] + public static void MinMagnitudeNumberTest(BFloat16 x, BFloat16 y, BFloat16 expectedResult) + { + AssertEqual(expectedResult, BFloat16.MinMagnitudeNumber(x, y), (BFloat16)0.0f); + } + + public static IEnumerable MinNumber_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.PositiveInfinity, BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NegativeInfinity, BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.MinValue, BFloat16.MaxValue, BFloat16.MinValue }; + yield return new object[] { BFloat16.MaxValue, BFloat16.MinValue, BFloat16.MinValue }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.NaN }; + yield return new object[] { BFloat16.NaN, (BFloat16)BFloat16.One, (BFloat16)BFloat16.One }; + yield return new object[] { (BFloat16)BFloat16.One, BFloat16.NaN, (BFloat16)BFloat16.One }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NaN, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.NaN, BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.NaN, BFloat16.PositiveInfinity, BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.NaN, BFloat16.NegativeInfinity, BFloat16.NegativeInfinity }; + yield return new object[] { (BFloat16)(-0.0f), (BFloat16)0.0f, (BFloat16)(-0.0f) }; + yield return new object[] { (BFloat16)0.0f, (BFloat16)(-0.0f), (BFloat16)(-0.0f) }; + yield return new object[] { (BFloat16)2.0f, (BFloat16)(-3.0f), (BFloat16)(-3.0f) }; + yield return new object[] { (BFloat16)(-3.0f), (BFloat16)2.0f, (BFloat16)(-3.0f) }; + yield return new object[] { (BFloat16)3.0f, (BFloat16)(-2.0f), (BFloat16)(-2.0f) }; + yield return new object[] { (BFloat16)(-2.0f), (BFloat16)3.0f, (BFloat16)(-2.0f) }; + } + + [Theory] + [MemberData(nameof(MinNumber_TestData))] + public static void MinNumberTest(BFloat16 x, BFloat16 y, BFloat16 expectedResult) + { + AssertEqual(expectedResult, BFloat16.MinNumber(x, y), (BFloat16)0.0f); + } + + public static IEnumerable ExpM1_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, (BFloat16)(-BFloat16.One), CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { (BFloat16)(-3.141f), (BFloat16)(-0.957f), CrossPlatformMachineEpsilon }; // value: -(pi) + yield return new object[] { (BFloat16)(-2.719f), (BFloat16)(-0.9336f), CrossPlatformMachineEpsilon }; // value: -(e) + yield return new object[] { (BFloat16)(-2.297f), (BFloat16)(-0.8984f), CrossPlatformMachineEpsilon }; // value: -(ln(10)) + yield return new object[] { (BFloat16)(-1.57f), (BFloat16)(-0.793f), CrossPlatformMachineEpsilon }; // value: -(pi / 2) + yield return new object[] { (BFloat16)(-1.445f), (BFloat16)(-0.7656f), CrossPlatformMachineEpsilon }; // value: -(log2(e)) + yield return new object[] { (BFloat16)(-1.414f), (BFloat16)(-0.7578f), CrossPlatformMachineEpsilon }; // value: -(sqrt(2)) + yield return new object[] { (BFloat16)(-1.125f), (BFloat16)(-0.6758f), CrossPlatformMachineEpsilon }; // value: -(2 / sqrt(pi)) + yield return new object[] { (BFloat16)(-BFloat16.One), (BFloat16)(-0.6328f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(-0.7852f), (BFloat16)(-0.543f), CrossPlatformMachineEpsilon }; // value: -(pi / 4) + yield return new object[] { (BFloat16)(-0.707f), (BFloat16)(-0.5078f), CrossPlatformMachineEpsilon }; // value: -(1 / sqrt(2)) + yield return new object[] { (BFloat16)(-0.6914f), (BFloat16)(-0.5f), CrossPlatformMachineEpsilon }; // value: -(ln(2)) + yield return new object[] { (BFloat16)(-0.6367f), (BFloat16)(-0.4707f), CrossPlatformMachineEpsilon }; // value: -(2 / pi) + yield return new object[] { (BFloat16)(-0.4336f), (BFloat16)(-0.3516f), CrossPlatformMachineEpsilon }; // value: -(log10(e)) + yield return new object[] { (BFloat16)(-0.3184f), (BFloat16)(-0.2734f), CrossPlatformMachineEpsilon }; // value: -(1 / pi) + yield return new object[] { (BFloat16)(-0.0f), (BFloat16)(0.0f), (BFloat16)0.0f }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0.0f), (BFloat16)(0.0f), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0.3184f), (BFloat16)(0.375f), CrossPlatformMachineEpsilon }; // value: (1 / pi) + yield return new object[] { (BFloat16)(0.4336f), (BFloat16)(0.543f), CrossPlatformMachineEpsilon }; // value: (log10(e)) + yield return new object[] { (BFloat16)(0.6367f), (BFloat16)(0.8906f), CrossPlatformMachineEpsilon }; // value: (2 / pi) + yield return new object[] { (BFloat16)(0.6914f), (BFloat16)(BFloat16.One), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (ln(2)) + yield return new object[] { (BFloat16)(0.707f), (BFloat16)(1.031f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (1 / sqrt(2)) + yield return new object[] { (BFloat16)(0.7852f), (BFloat16)(1.195f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (pi / 4) + yield return new object[] { (BFloat16)(BFloat16.One), (BFloat16)(1.719f), CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { (BFloat16)(1.125f), (BFloat16)(2.078f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)(1.414f), (BFloat16)(3.109f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (sqrt(2)) + yield return new object[] { (BFloat16)(1.445f), (BFloat16)(3.25f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (log2(e)) + yield return new object[] { (BFloat16)(1.57f), (BFloat16)(3.812f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (pi / 2) + yield return new object[] { (BFloat16)(2.297f), (BFloat16)(8.938f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (ln(10)) + yield return new object[] { (BFloat16)(2.719f), (BFloat16)(14.19f), CrossPlatformMachineEpsilon * (BFloat16)100 }; // value: (e) + yield return new object[] { (BFloat16)(3.141f), (BFloat16)(22.12f), CrossPlatformMachineEpsilon * (BFloat16)100 }; // value: (pi) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, 0.0 }; + } + + [Theory] + [MemberData(nameof(ExpM1_TestData))] + public static void ExpM1Test(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(expectedResult, BFloat16.ExpM1(value), allowedVariance); + } + + public static IEnumerable Exp2_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, (BFloat16)(0.0f), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-3.141f), (BFloat16)(0.1133f), CrossPlatformMachineEpsilon }; // value: -(pi) + yield return new object[] { (BFloat16)(-2.719f), (BFloat16)(0.1523f), CrossPlatformMachineEpsilon }; // value: -(e) + yield return new object[] { (BFloat16)(-2.297f), (BFloat16)(0.2031f), CrossPlatformMachineEpsilon }; // value: -(ln(10)) + yield return new object[] { (BFloat16)(-1.57f), (BFloat16)(0.3359f), CrossPlatformMachineEpsilon }; // value: -(pi / 2) + yield return new object[] { (BFloat16)(-1.445f), (BFloat16)(0.3672f), CrossPlatformMachineEpsilon }; // value: -(log2(e)) + yield return new object[] { (BFloat16)(-1.414f), (BFloat16)(0.375f), CrossPlatformMachineEpsilon }; // value: -(sqrt(2)) + yield return new object[] { (BFloat16)(-1.125f), (BFloat16)(0.459f), CrossPlatformMachineEpsilon }; // value: -(2 / sqrt(pi)) + yield return new object[] { (BFloat16)(-BFloat16.One), (BFloat16)(0.5f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(-0.7852f), (BFloat16)(0.582f), CrossPlatformMachineEpsilon }; // value: -(pi / 4) + yield return new object[] { (BFloat16)(-0.707f), (BFloat16)(0.6133f), CrossPlatformMachineEpsilon }; // value: -(1 / sqrt(2)) + yield return new object[] { (BFloat16)(-0.6914f), (BFloat16)(0.6211f), CrossPlatformMachineEpsilon }; // value: -(ln(2)) + yield return new object[] { (BFloat16)(-0.6367f), (BFloat16)(0.6445f), CrossPlatformMachineEpsilon }; // value: -(2 / pi) + yield return new object[] { (BFloat16)(-0.4336f), (BFloat16)(0.7422f), CrossPlatformMachineEpsilon }; // value: -(log10(e)) + yield return new object[] { (BFloat16)(-0.3184f), (BFloat16)(0.8008f), CrossPlatformMachineEpsilon }; // value: -(1 / pi) + yield return new object[] { (BFloat16)(-0.0f), (BFloat16)(BFloat16.One), (BFloat16)0.0f }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0.0f), (BFloat16)(BFloat16.One), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0.3184f), (BFloat16)(1.25f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (1 / pi) + yield return new object[] { (BFloat16)(0.4336f), (BFloat16)(1.352f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (log10(e)) + yield return new object[] { (BFloat16)(0.6367f), (BFloat16)(1.555f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (2 / pi) + yield return new object[] { (BFloat16)(0.6914f), (BFloat16)(1.617f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (ln(2)) + yield return new object[] { (BFloat16)(0.707f), (BFloat16)(1.633f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (1 / sqrt(2)) + yield return new object[] { (BFloat16)(0.7852f), (BFloat16)(1.727f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (pi / 4) + yield return new object[] { (BFloat16)(BFloat16.One), (BFloat16)(2.0f), CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { (BFloat16)(1.125f), (BFloat16)(2.188f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)(1.414f), (BFloat16)(2.672f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (sqrt(2)) + yield return new object[] { (BFloat16)(1.445f), (BFloat16)(2.719f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (log2(e)) + yield return new object[] { (BFloat16)(1.57f), (BFloat16)(2.969f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (pi / 2) + yield return new object[] { (BFloat16)(2.297f), (BFloat16)(4.906f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (ln(10)) + yield return new object[] { (BFloat16)(2.719f), (BFloat16)(6.594f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (e) + yield return new object[] { (BFloat16)(3.141f), (BFloat16)(8.812f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (pi) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, 0.0f }; + } + + [Theory] + [MemberData(nameof(Exp2_TestData))] + public static void Exp2Test(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(expectedResult, BFloat16.Exp2(value), allowedVariance); + } + + public static IEnumerable Exp2M1_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, (BFloat16)(-BFloat16.One), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-3.141f), (BFloat16)(-0.8867f), CrossPlatformMachineEpsilon }; // value: -(pi) + yield return new object[] { (BFloat16)(-2.719f), (BFloat16)(-0.8477f), CrossPlatformMachineEpsilon }; // value: -(e) + yield return new object[] { (BFloat16)(-2.297f), (BFloat16)(-0.7969f), CrossPlatformMachineEpsilon }; // value: -(ln(10)) + yield return new object[] { (BFloat16)(-1.57f), (BFloat16)(-0.6641f), CrossPlatformMachineEpsilon }; // value: -(pi / 2) + yield return new object[] { (BFloat16)(-1.445f), (BFloat16)(-0.6328f), CrossPlatformMachineEpsilon }; // value: -(log2(e)) + yield return new object[] { (BFloat16)(-1.414f), (BFloat16)(-0.625f), CrossPlatformMachineEpsilon }; // value: -(sqrt(2)) + yield return new object[] { (BFloat16)(-1.125f), (BFloat16)(-0.543f), CrossPlatformMachineEpsilon }; // value: -(2 / sqrt(pi)) + yield return new object[] { (BFloat16)(-BFloat16.One), (BFloat16)(-0.5f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(-0.7852f), (BFloat16)(-0.4199f), CrossPlatformMachineEpsilon }; // value: -(pi / 4) + yield return new object[] { (BFloat16)(-0.707f), (BFloat16)(-0.3867f), CrossPlatformMachineEpsilon }; // value: -(1 / sqrt(2)) + yield return new object[] { (BFloat16)(-0.6914f), (BFloat16)(-0.3809f), CrossPlatformMachineEpsilon }; // value: -(ln(2)) + yield return new object[] { (BFloat16)(-0.6367f), (BFloat16)(-0.3574f), CrossPlatformMachineEpsilon }; // value: -(2 / pi) + yield return new object[] { (BFloat16)(-0.4336f), (BFloat16)(-0.2598f), CrossPlatformMachineEpsilon }; // value: -(log10(e)) + yield return new object[] { (BFloat16)(-0.3184f), (BFloat16)(-0.1982f), CrossPlatformMachineEpsilon }; // value: -(1 / pi) + yield return new object[] { (BFloat16)(-0f), (BFloat16)(0f), (BFloat16)0.0f }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0f), (BFloat16)(0f), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0.3184f), (BFloat16)(0.2471f), CrossPlatformMachineEpsilon }; // value: (1 / pi) + yield return new object[] { (BFloat16)(0.4336f), (BFloat16)(0.3516f), CrossPlatformMachineEpsilon }; // value: (log10(e)) + yield return new object[] { (BFloat16)(0.6367f), (BFloat16)(0.5547f), CrossPlatformMachineEpsilon }; // value: (2 / pi) + yield return new object[] { (BFloat16)(0.6914f), (BFloat16)(0.6133f), CrossPlatformMachineEpsilon }; // value: (ln(2)) + yield return new object[] { (BFloat16)(0.707f), (BFloat16)(0.6328f), CrossPlatformMachineEpsilon }; // value: (1 / sqrt(2)) + yield return new object[] { (BFloat16)(0.7852f), (BFloat16)(0.7227f), CrossPlatformMachineEpsilon }; // value: (pi / 4) + yield return new object[] { (BFloat16)(BFloat16.One), (BFloat16)(BFloat16.One), CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { (BFloat16)(1.125f), (BFloat16)(1.18f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)(1.414f), (BFloat16)(1.664f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (sqrt(2)) + yield return new object[] { (BFloat16)(1.445f), (BFloat16)(1.727f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (log2(e)) + yield return new object[] { (BFloat16)(1.57f), (BFloat16)(1.969f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (pi / 2) + yield return new object[] { (BFloat16)(2.297f), (BFloat16)(3.906f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (ln(10)) + yield return new object[] { (BFloat16)(2.719f), (BFloat16)(5.594f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (e) + yield return new object[] { (BFloat16)(3.141f), (BFloat16)(7.812f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (pi) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, (BFloat16)0.0f }; + } + + [Theory] + [MemberData(nameof(Exp2M1_TestData))] + public static void Exp2M1Test(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(expectedResult, BFloat16.Exp2M1(value), allowedVariance); + } + + public static IEnumerable Exp10_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, (BFloat16)0.0f, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-3.141f), (BFloat16)0.0007248f, CrossPlatformMachineEpsilon / (BFloat16)1000 }; // value: -(pi) + yield return new object[] { (BFloat16)(-2.719f), (BFloat16)0.001907f, CrossPlatformMachineEpsilon / (BFloat16)100 }; // value: -(e) + yield return new object[] { (BFloat16)(-2.297f), (BFloat16)0.005035f, CrossPlatformMachineEpsilon / (BFloat16)100 }; // value: -(ln(10)) + yield return new object[] { (BFloat16)(-1.57f), (BFloat16)0.02686f, CrossPlatformMachineEpsilon / (BFloat16)10 }; // value: -(pi / 2) + yield return new object[] { (BFloat16)(-1.445f), (BFloat16)0.03589f, CrossPlatformMachineEpsilon / (BFloat16)10 }; // value: -(log2(e)) + yield return new object[] { (BFloat16)(-1.414f), (BFloat16)0.03857f, CrossPlatformMachineEpsilon / (BFloat16)10 }; // value: -(sqrt(2)) + yield return new object[] { (BFloat16)(-1.125f), (BFloat16)0.0752f, CrossPlatformMachineEpsilon / (BFloat16)10 }; // value: -(2 / sqrt(pi)) + yield return new object[] { (BFloat16)(-BFloat16.One), (BFloat16)0.1f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(-0.7852f), (BFloat16)0.1641f, CrossPlatformMachineEpsilon }; // value: -(pi / 4) + yield return new object[] { (BFloat16)(-0.707f), (BFloat16)0.1963f, CrossPlatformMachineEpsilon }; // value: -(1 / sqrt(2)) + yield return new object[] { (BFloat16)(-0.6914f), (BFloat16)0.2031f, CrossPlatformMachineEpsilon }; // value: -(ln(2)) + yield return new object[] { (BFloat16)(-0.6367f), (BFloat16)0.2305f, CrossPlatformMachineEpsilon }; // value: -(2 / pi) + yield return new object[] { (BFloat16)(-0.4336f), (BFloat16)0.3691f, CrossPlatformMachineEpsilon }; // value: -(log10(e)) + yield return new object[] { (BFloat16)(-0.3184f), (BFloat16)0.4805f, CrossPlatformMachineEpsilon }; // value: -(1 / pi) + yield return new object[] { (BFloat16)(-0.0f), (BFloat16)BFloat16.One, (BFloat16)0.0f }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0.0f), (BFloat16)BFloat16.One, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0.3184f), (BFloat16)2.078f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (1 / pi) + yield return new object[] { (BFloat16)(0.4336f), (BFloat16)2.719f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (log10(e)) + yield return new object[] { (BFloat16)(0.6367f), (BFloat16)4.344f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (2 / pi) + yield return new object[] { (BFloat16)(0.6914f), (BFloat16)4.906f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (ln(2)) + yield return new object[] { (BFloat16)(0.707f), (BFloat16)5.094f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (1 / sqrt(2)) + yield return new object[] { (BFloat16)(0.7852f), (BFloat16)6.094f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (pi / 4) + yield return new object[] { (BFloat16)(BFloat16.One), (BFloat16)10.0f, CrossPlatformMachineEpsilon * (BFloat16)100 }; + yield return new object[] { (BFloat16)(1.125f), (BFloat16)13.31f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // value: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)(1.414f), (BFloat16)26f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // value: (sqrt(2)) + yield return new object[] { (BFloat16)(1.445f), (BFloat16)27.88f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // value: (log2(e)) + yield return new object[] { (BFloat16)(1.57f), (BFloat16)37.25f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // value: (pi / 2) + yield return new object[] { (BFloat16)(2.297f), (BFloat16)198f, CrossPlatformMachineEpsilon * (BFloat16)1000 }; // value: (ln(10)) + yield return new object[] { (BFloat16)(2.719f), (BFloat16)524f, CrossPlatformMachineEpsilon * (BFloat16)1000 }; // value: (e) + yield return new object[] { (BFloat16)(3.141f), (BFloat16)1384f, CrossPlatformMachineEpsilon * (BFloat16)10000 }; // value: (pi) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, (BFloat16)0.0f }; + } + + [Theory] + [MemberData(nameof(Exp10_TestData))] + public static void Exp10Test(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(expectedResult, BFloat16.Exp10(value), allowedVariance); + } + + public static IEnumerable Exp10M1_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, (BFloat16)(-BFloat16.One), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-3.141f), (BFloat16)(-1f), CrossPlatformMachineEpsilon }; // value: -(pi) + yield return new object[] { (BFloat16)(-2.719f), (BFloat16)(-1f), CrossPlatformMachineEpsilon }; // value: -(e) + yield return new object[] { (BFloat16)(-2.297f), (BFloat16)(-0.9961f), CrossPlatformMachineEpsilon }; // value: -(ln(10)) + yield return new object[] { (BFloat16)(-1.57f), (BFloat16)(-0.9727f), CrossPlatformMachineEpsilon }; // value: -(pi / 2) + yield return new object[] { (BFloat16)(-1.445f), (BFloat16)(-0.9648f), CrossPlatformMachineEpsilon }; // value: -(log2(e)) + yield return new object[] { (BFloat16)(-1.414f), (BFloat16)(-0.9609f), CrossPlatformMachineEpsilon }; // value: -(sqrt(2)) + yield return new object[] { (BFloat16)(-1.125f), (BFloat16)(-0.9258f), CrossPlatformMachineEpsilon }; // value: -(2 / sqrt(pi)) + yield return new object[] { (BFloat16)(-BFloat16.One), (BFloat16)(-0.9f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(-0.7852f), (BFloat16)(-0.8359f), CrossPlatformMachineEpsilon }; // value: -(pi / 4) + yield return new object[] { (BFloat16)(-0.707f), (BFloat16)(-0.8047f), CrossPlatformMachineEpsilon }; // value: -(1 / sqrt(2)) + yield return new object[] { (BFloat16)(-0.6914f), (BFloat16)(-0.7969f), CrossPlatformMachineEpsilon }; // value: -(ln(2)) + yield return new object[] { (BFloat16)(-0.6367f), (BFloat16)(-0.7695f), CrossPlatformMachineEpsilon }; // value: -(2 / pi) + yield return new object[] { (BFloat16)(-0.4336f), (BFloat16)(-0.6328f), CrossPlatformMachineEpsilon }; // value: -(log10(e)) + yield return new object[] { (BFloat16)(-0.3184f), (BFloat16)(-0.5195f), CrossPlatformMachineEpsilon }; // value: -(1 / pi) + yield return new object[] { (BFloat16)(-0f), (BFloat16)(0f), (BFloat16)0.0f }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0f), (BFloat16)(0f), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0.3184f), (BFloat16)(1.078f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (1 / pi) + yield return new object[] { (BFloat16)(0.4336f), (BFloat16)(1.711f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (log10(e)) + yield return new object[] { (BFloat16)(0.6367f), (BFloat16)(3.328f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (2 / pi) + yield return new object[] { (BFloat16)(0.6914f), (BFloat16)(3.906f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (ln(2)) + yield return new object[] { (BFloat16)(0.707f), (BFloat16)(4.094f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (1 / sqrt(2)) + yield return new object[] { (BFloat16)(0.7852f), (BFloat16)(5.094f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (pi / 4) + yield return new object[] { (BFloat16)(BFloat16.One), (BFloat16)(9.0f), CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { (BFloat16)(1.125f), (BFloat16)(12.31f), CrossPlatformMachineEpsilon * (BFloat16)100 }; // value: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)(1.414f), (BFloat16)(25f), CrossPlatformMachineEpsilon * (BFloat16)100 }; // value: (sqrt(2)) + yield return new object[] { (BFloat16)(1.445f), (BFloat16)(26.88f), CrossPlatformMachineEpsilon * (BFloat16)100 }; // value: (log2(e)) + yield return new object[] { (BFloat16)(1.57f), (BFloat16)(36.25f), CrossPlatformMachineEpsilon * (BFloat16)100 }; // value: (pi / 2) + yield return new object[] { (BFloat16)(2.297f), (BFloat16)(197f), CrossPlatformMachineEpsilon * (BFloat16)1000 }; // value: (ln(10)) + yield return new object[] { (BFloat16)(2.719f), (BFloat16)(524f), CrossPlatformMachineEpsilon * (BFloat16)1000 }; // value: (e) + yield return new object[] { (BFloat16)(3.141f), (BFloat16)(1384f), CrossPlatformMachineEpsilon * (BFloat16)10000 }; // value: (pi) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, (BFloat16)0.0f }; + } + + [Theory] + [MemberData(nameof(Exp10M1_TestData))] + public static void Exp10M1Test(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(expectedResult, BFloat16.Exp10M1(value), allowedVariance); + } + + public static IEnumerable LogP1_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.NaN, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-3.141f), BFloat16.NaN, (BFloat16)0.0f }; // value: -(pi) + yield return new object[] { (BFloat16)(-2.719f), BFloat16.NaN, (BFloat16)0.0f }; // value: -(e) + yield return new object[] { (BFloat16)(-1.414f), BFloat16.NaN, (BFloat16)0.0f }; // value: -(sqrt(2)) + yield return new object[] { BFloat16.NaN, BFloat16.NaN, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-BFloat16.One), BFloat16.NegativeInfinity, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-0.957f), (BFloat16)(-3.141f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(pi) + yield return new object[] { (BFloat16)(-0.9336f), (BFloat16)(-2.719f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(e) + yield return new object[] { (BFloat16)(-0.8984f), (BFloat16)(-2.281f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(ln(10)) + yield return new object[] { (BFloat16)(-0.793f), (BFloat16)(-1.578f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(pi / 2) + yield return new object[] { (BFloat16)(-0.7656f), (BFloat16)(-1.453f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(log2(e)) + yield return new object[] { (BFloat16)(-0.7578f), (BFloat16)(-1.422f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(sqrt(2)) + yield return new object[] { (BFloat16)(-0.6758f), (BFloat16)(-1.125f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(2 / sqrt(pi)) + yield return new object[] { (BFloat16)(-0.6321f), (BFloat16)(-BFloat16.One), CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { (BFloat16)(-0.543f), (BFloat16)(-0.7812f), CrossPlatformMachineEpsilon }; // expected: -(pi / 4) + yield return new object[] { (BFloat16)(-0.5078f), (BFloat16)(-0.707f), CrossPlatformMachineEpsilon }; // expected: -(1 / sqrt(2)) + yield return new object[] { (BFloat16)(-0.5f), (BFloat16)(-0.6914f), CrossPlatformMachineEpsilon }; // expected: -(ln(2)) + yield return new object[] { (BFloat16)(-0.4707f), (BFloat16)(-0.6367f), CrossPlatformMachineEpsilon }; // expected: -(2 / pi) + yield return new object[] { (BFloat16)(-0f), (BFloat16)(0f), 0.0f }; + yield return new object[] { (BFloat16)(0f), (BFloat16)(0f), 0.0f }; + yield return new object[] { (BFloat16)(0.375f), (BFloat16)(0.3184f), CrossPlatformMachineEpsilon }; // expected: (1 / pi) + yield return new object[] { (BFloat16)(0.543f), (BFloat16)(0.4336f), CrossPlatformMachineEpsilon }; // expected: (log10(e)) + yield return new object[] { (BFloat16)(0.8906f), (BFloat16)(0.6367f), CrossPlatformMachineEpsilon }; // expected: (2 / pi) + yield return new object[] { (BFloat16)(BFloat16.One), (BFloat16)(0.6931f), CrossPlatformMachineEpsilon }; // expected: (ln(2)) + yield return new object[] { (BFloat16)(1.031f), (BFloat16)(0.707f), CrossPlatformMachineEpsilon }; // expected: (1 / sqrt(2)) + yield return new object[] { (BFloat16)(1.195f), (BFloat16)(0.7852f), CrossPlatformMachineEpsilon }; // expected: (pi / 4) + yield return new object[] { (BFloat16)(1.719f), (BFloat16)(BFloat16.One), CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { (BFloat16)(2.094f), (BFloat16)(1.133f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)(3.109f), (BFloat16)(1.414f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (sqrt(2)) + yield return new object[] { (BFloat16)(3.234f), (BFloat16)(1.445f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (log2(e)) + yield return new object[] { (BFloat16)(3.812f), (BFloat16)(1.57f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (pi / 2) + yield return new object[] { (BFloat16)(9f), (BFloat16)(2.297f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (ln(10)) + yield return new object[] { (BFloat16)(14.12f), (BFloat16)(2.719f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (e) + yield return new object[] { (BFloat16)(22.12f), (BFloat16)(3.141f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (pi) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, (BFloat16)0.0f }; + } + + [Theory] + [MemberData(nameof(LogP1_TestData))] + public static void LogP1Test(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(expectedResult, BFloat16.LogP1(value), allowedVariance); + } + + public static IEnumerable Log2P1_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.NaN, (BFloat16)0.0f }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-BFloat16.One), BFloat16.NegativeInfinity, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-0.8867f), (BFloat16)(-3.141f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(pi) + yield return new object[] { (BFloat16)(-0.8477f), (BFloat16)(-2.719f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(e) + yield return new object[] { (BFloat16)(-0.7969f), (BFloat16)(-2.297f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(ln(10)) + yield return new object[] { (BFloat16)(-0.6641f), (BFloat16)(-1.57f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(pi / 2) + yield return new object[] { (BFloat16)(-0.6328f), (BFloat16)(-1.445f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(log2(e)) + yield return new object[] { (BFloat16)(-0.625f), (BFloat16)(-1.414f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(sqrt(2)) + yield return new object[] { (BFloat16)(-0.543f), (BFloat16)(-1.133f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(2 / sqrt(pi)) + yield return new object[] { (BFloat16)(-0.5f), (BFloat16)(-BFloat16.One), CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { (BFloat16)(-0.4199f), (BFloat16)(-0.7852f), CrossPlatformMachineEpsilon }; // expected: -(pi / 4) + yield return new object[] { (BFloat16)(-0.3867f), (BFloat16)(-0.707f), CrossPlatformMachineEpsilon }; // expected: -(1 / sqrt(2)) + yield return new object[] { (BFloat16)(-0.3809f), (BFloat16)(-0.6914f), CrossPlatformMachineEpsilon }; // expected: -(ln(2)) + yield return new object[] { (BFloat16)(-0.3574f), (BFloat16)(-0.6367f), CrossPlatformMachineEpsilon }; // expected: -(2 / pi) + yield return new object[] { (BFloat16)(-0.2598f), (BFloat16)(-0.4336f), CrossPlatformMachineEpsilon }; // expected: -(log10(e)) + yield return new object[] { (BFloat16)(-0.1982f), (BFloat16)(-0.3184f), CrossPlatformMachineEpsilon }; // expected: -(1 / pi) + yield return new object[] { (BFloat16)(-0f), (BFloat16)(0f), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0f), (BFloat16)(0f), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0.2471f), (BFloat16)(0.3184f), CrossPlatformMachineEpsilon }; // expected: (1 / pi) + yield return new object[] { (BFloat16)(0.3516f), (BFloat16)(0.4355f), CrossPlatformMachineEpsilon }; // expected: (log10(e)) + yield return new object[] { (BFloat16)(0.5547f), (BFloat16)(0.6367f), CrossPlatformMachineEpsilon }; // expected: (2 / pi) + yield return new object[] { (BFloat16)(0.6172f), (BFloat16)(0.6953f), CrossPlatformMachineEpsilon }; // expected: (ln(2)) + yield return new object[] { (BFloat16)(0.6328f), (BFloat16)(0.707f), CrossPlatformMachineEpsilon }; // expected: (1 / sqrt(2)) + yield return new object[] { (BFloat16)(0.7227f), (BFloat16)(0.7852f), CrossPlatformMachineEpsilon }; // expected: (pi / 4) + yield return new object[] { (BFloat16)(BFloat16.One), (BFloat16)(BFloat16.One), CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { (BFloat16)(1.188f), (BFloat16)(1.133f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)(1.664f), (BFloat16)(1.414f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (sqrt(2)) + yield return new object[] { (BFloat16)(1.719f), (BFloat16)(1.445f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (log2(e)) + yield return new object[] { (BFloat16)(1.969f), (BFloat16)(1.57f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (pi / 2) + yield return new object[] { (BFloat16)(3.938f), (BFloat16)(2.297f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (ln(10)) + yield return new object[] { (BFloat16)(5.594f), (BFloat16)(2.719f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (e) + yield return new object[] { (BFloat16)(7.812f), (BFloat16)(3.141f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (pi) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, (BFloat16)0.0f }; + } + + [Theory] + [MemberData(nameof(Log2P1_TestData))] + public static void Log2P1Test(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(expectedResult, BFloat16.Log2P1(value), allowedVariance); + } + + public static IEnumerable Log10P1_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.NaN, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-3.141f), BFloat16.NaN, (BFloat16)0.0f }; // value: -(pi) + yield return new object[] { (BFloat16)(-2.719f), BFloat16.NaN, (BFloat16)0.0f }; // value: -(e) + yield return new object[] { (BFloat16)(-1.414f), BFloat16.NaN, (BFloat16)0.0f }; // value: -(sqrt(2)) + yield return new object[] { BFloat16.NaN, BFloat16.NaN, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-BFloat16.One), BFloat16.NegativeInfinity, (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(-0.9961f), (BFloat16)(-2.406f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(ln(10)) + yield return new object[] { (BFloat16)(-0.9727f), (BFloat16)(-1.562f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(pi / 2) + yield return new object[] { (BFloat16)(-0.9648f), (BFloat16)(-1.453f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(log2(e)) + yield return new object[] { (BFloat16)(-0.9609f), (BFloat16)(-1.406f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(sqrt(2)) + yield return new object[] { (BFloat16)(-0.9258f), (BFloat16)(-1.133f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: -(2 / sqrt(pi)) + yield return new object[] { (BFloat16)(-0.8984f), (BFloat16)(-0.9922f), CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { (BFloat16)(-0.8359f), (BFloat16)(-0.7852f), CrossPlatformMachineEpsilon }; // expected: -(pi / 4) + yield return new object[] { (BFloat16)(-0.8047f), (BFloat16)(-0.7109f), CrossPlatformMachineEpsilon }; // expected: -(1 / sqrt(2)) + yield return new object[] { (BFloat16)(-0.7969f), (BFloat16)(-0.6914f), CrossPlatformMachineEpsilon }; // expected: -(ln(2)) + yield return new object[] { (BFloat16)(-0.7695f), (BFloat16)(-0.6367f), CrossPlatformMachineEpsilon }; // expected: -(2 / pi) + yield return new object[] { (BFloat16)(-0.6328f), (BFloat16)(-0.4355f), CrossPlatformMachineEpsilon }; // expected: -(log10(e)) + yield return new object[] { (BFloat16)(-0.5195f), (BFloat16)(-0.3184f), CrossPlatformMachineEpsilon }; // expected: -(1 / pi) + yield return new object[] { (BFloat16)(-0f), (BFloat16)(0f), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(0f), (BFloat16)(0f), (BFloat16)0.0f }; + yield return new object[] { (BFloat16)(1.078f), (BFloat16)(0.3184f), CrossPlatformMachineEpsilon }; // expected: (1 / pi) + yield return new object[] { (BFloat16)(1.719f), (BFloat16)(0.4336f), CrossPlatformMachineEpsilon }; // expected: (log10(e)) value: (e) + yield return new object[] { (BFloat16)(3.328f), (BFloat16)(0.6367f), CrossPlatformMachineEpsilon }; // expected: (2 / pi) + yield return new object[] { (BFloat16)(3.938f), (BFloat16)(0.6953f), CrossPlatformMachineEpsilon }; // expected: (ln(2)) + yield return new object[] { (BFloat16)(4.094f), (BFloat16)(0.707f), CrossPlatformMachineEpsilon }; // expected: (1 / sqrt(2)) + yield return new object[] { (BFloat16)(5.094f), (BFloat16)(0.7852f), CrossPlatformMachineEpsilon }; // expected: (pi / 4) + yield return new object[] { (BFloat16)(9.0f), (BFloat16)(BFloat16.One), CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { (BFloat16)(12.44f), (BFloat16)(1.125f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)(25f), (BFloat16)(1.414f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (sqrt(2)) + yield return new object[] { (BFloat16)(26.75f), (BFloat16)(1.445f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (log2(e)) + yield return new object[] { (BFloat16)(36.25f), (BFloat16)(1.57f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (pi / 2) + yield return new object[] { (BFloat16)(200f), (BFloat16)(2.297f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (ln(10)) + yield return new object[] { (BFloat16)(520f), (BFloat16)(2.719f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (e) + yield return new object[] { (BFloat16)(1384f), (BFloat16)(3.141f), CrossPlatformMachineEpsilon * (BFloat16)10 }; // expected: (pi) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, (BFloat16)0.0f }; + } + + [Theory] + [MemberData(nameof(Log10P1_TestData))] + public static void Log10P1Test(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(expectedResult, BFloat16.Log10P1(value), allowedVariance); + } + + public static IEnumerable Hypot_TestData() + { + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, BFloat16.Zero, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, BFloat16.One, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, BFloat16.E, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, (BFloat16)10.0f, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, BFloat16.Zero, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, BFloat16.One, BFloat16.One, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, (BFloat16)1.57f, (BFloat16)1.57f, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, (BFloat16)2.0f, (BFloat16)2.0f, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, BFloat16.E, BFloat16.E, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, (BFloat16)3.0f, (BFloat16)3.0f, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, (BFloat16)10.0f, (BFloat16)10.0f, BFloat16.Zero }; + yield return new object[] { BFloat16.One, BFloat16.One, (BFloat16)1.41421356f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { BFloat16.E, (BFloat16)0.3184f, (BFloat16)2.734f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (1 / pi) + yield return new object[] { BFloat16.E, (BFloat16)0.4336f, (BFloat16)2.75f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (log10(e)) + yield return new object[] { BFloat16.E, (BFloat16)0.6367f, (BFloat16)2.797f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (2 / pi) + yield return new object[] { BFloat16.E, (BFloat16)0.6914f, (BFloat16)2.812f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (ln(2)) + yield return new object[] { BFloat16.E, (BFloat16)0.707f, (BFloat16)2.812f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (1 / sqrt(2)) + yield return new object[] { BFloat16.E, (BFloat16)0.7852f, (BFloat16)2.828f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (pi / 4) + yield return new object[] { BFloat16.E, BFloat16.One, (BFloat16)2.896f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) + yield return new object[] { BFloat16.E, (BFloat16)1.125f, (BFloat16)2.938f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (2 / sqrt(pi)) + yield return new object[] { BFloat16.E, (BFloat16)1.414f, (BFloat16)3.062f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (sqrt(2)) + yield return new object[] { BFloat16.E, (BFloat16)1.445f, (BFloat16)3.078f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (log2(e)) + yield return new object[] { BFloat16.E, (BFloat16)1.57f, (BFloat16)3.141f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (pi / 2) + yield return new object[] { BFloat16.E, (BFloat16)2.297f, (BFloat16)3.562f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (ln(10)) + yield return new object[] { BFloat16.E, BFloat16.E, (BFloat16)3.844f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (e) + yield return new object[] { BFloat16.E, (BFloat16)3.141f, (BFloat16)4.156f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // x: (e) y: (pi) + yield return new object[] { (BFloat16)10.0f, (BFloat16)0.3184f, (BFloat16)10.0f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (1 / pi) + yield return new object[] { (BFloat16)10.0f, (BFloat16)0.4336f, (BFloat16)10.0f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (log10(e)) + yield return new object[] { (BFloat16)10.0f, (BFloat16)0.6367f, (BFloat16)10.0f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (2 / pi) + yield return new object[] { (BFloat16)10.0f, (BFloat16)0.6914f, (BFloat16)10.0f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (ln(2)) + yield return new object[] { (BFloat16)10.0f, (BFloat16)0.707f, (BFloat16)10.0f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (1 / sqrt(2)) + yield return new object[] { (BFloat16)10.0f, (BFloat16)0.7852f, (BFloat16)10.0f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (pi / 4) + yield return new object[] { (BFloat16)10.0f, BFloat16.One, (BFloat16)10.05f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // + yield return new object[] { (BFloat16)10.0f, (BFloat16)1.125f, (BFloat16)10.06f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)10.0f, (BFloat16)1.414f, (BFloat16)10.12f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (sqrt(2)) + yield return new object[] { (BFloat16)10.0f, (BFloat16)1.445f, (BFloat16)10.12f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (log2(e)) + yield return new object[] { (BFloat16)10.0f, (BFloat16)1.57f, (BFloat16)10.12f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (pi / 2) + yield return new object[] { (BFloat16)10.0f, (BFloat16)2.297f, (BFloat16)10.25f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (ln(10)) + yield return new object[] { (BFloat16)10.0f, BFloat16.E, (BFloat16)10.36f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (e) + yield return new object[] { (BFloat16)10.0f, (BFloat16)3.141f, (BFloat16)10.5f, CrossPlatformMachineEpsilon * (BFloat16)100 }; // y: (pi) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NaN, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.Zero, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.One, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.E, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, 10.0f, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, BFloat16.Zero }; + } + + [Theory] + [MemberData(nameof(Hypot_TestData))] + public static void Hypot(BFloat16 x, BFloat16 y, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(expectedResult, BFloat16.Hypot(-x, -y), allowedVariance); + AssertEqual(expectedResult, BFloat16.Hypot(-x, +y), allowedVariance); + AssertEqual(expectedResult, BFloat16.Hypot(+x, -y), allowedVariance); + AssertEqual(expectedResult, BFloat16.Hypot(+x, +y), allowedVariance); + + AssertEqual(expectedResult, BFloat16.Hypot(-y, -x), allowedVariance); + AssertEqual(expectedResult, BFloat16.Hypot(-y, +x), allowedVariance); + AssertEqual(expectedResult, BFloat16.Hypot(+y, -x), allowedVariance); + AssertEqual(expectedResult, BFloat16.Hypot(+y, +x), allowedVariance); + } + + public static IEnumerable RootN_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, -5, -BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.NegativeInfinity, -4, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NegativeInfinity, -3, -BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.NegativeInfinity, -2, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NegativeInfinity, -1, -BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.NegativeInfinity, 0, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NegativeInfinity, 1, BFloat16.NegativeInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.NegativeInfinity, 2, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NegativeInfinity, 3, BFloat16.NegativeInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.NegativeInfinity, 4, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NegativeInfinity, 5, BFloat16.NegativeInfinity, BFloat16.Zero }; + yield return new object[] { -BFloat16.E, -5, -(BFloat16)0.81873075f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { -BFloat16.E, -4, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { -BFloat16.E, -3, -(BFloat16)0.71653131f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { -BFloat16.E, -2, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { -BFloat16.E, -1, -(BFloat16)0.36787944f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { -BFloat16.E, 0, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { -BFloat16.E, 1, -BFloat16.E, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { -BFloat16.E, 2, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { -BFloat16.E, 3, -(BFloat16)1.39561243f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { -BFloat16.E, 4, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { -BFloat16.E, 5, -(BFloat16)1.22140276f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { -BFloat16.One, -5, -BFloat16.One, BFloat16.Zero }; + yield return new object[] { -BFloat16.One, -4, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { -BFloat16.One, -3, -BFloat16.One, BFloat16.Zero }; + yield return new object[] { -BFloat16.One, -2, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { -BFloat16.One, -1, -BFloat16.One, BFloat16.Zero }; + yield return new object[] { -BFloat16.One, 0, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { -BFloat16.One, 1, -BFloat16.One, BFloat16.Zero }; + yield return new object[] { -BFloat16.One, 2, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { -BFloat16.One, 3, -BFloat16.One, BFloat16.Zero }; + yield return new object[] { -BFloat16.One, 4, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { -BFloat16.One, 5, -BFloat16.One, BFloat16.Zero }; + yield return new object[] { -BFloat16.Zero, -5, BFloat16.NegativeInfinity, BFloat16.Zero }; + yield return new object[] { -BFloat16.Zero, -4, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { -BFloat16.Zero, -3, BFloat16.NegativeInfinity, BFloat16.Zero }; + yield return new object[] { -BFloat16.Zero, -2, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { -BFloat16.Zero, -1, BFloat16.NegativeInfinity, BFloat16.Zero }; + yield return new object[] { -BFloat16.Zero, 0, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { -BFloat16.Zero, 1, -BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { -BFloat16.Zero, 2, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { -BFloat16.Zero, 3, -BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { -BFloat16.Zero, 4, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { -BFloat16.Zero, 5, -BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, -5, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, -4, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, -3, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, -2, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, -1, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, 0, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, 1, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, 2, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, 3, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, 4, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.NaN, 5, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, -5, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, -4, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, -3, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, -2, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, -1, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, 0, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, 1, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, 2, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, 3, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, 4, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, 5, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.One, -5, BFloat16.One, BFloat16.Zero }; + yield return new object[] { BFloat16.One, -4, BFloat16.One, BFloat16.Zero }; + yield return new object[] { BFloat16.One, -3, BFloat16.One, BFloat16.Zero }; + yield return new object[] { BFloat16.One, -2, BFloat16.One, BFloat16.Zero }; + yield return new object[] { BFloat16.One, -1, BFloat16.One, BFloat16.Zero }; + yield return new object[] { BFloat16.One, 0, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.One, 1, BFloat16.One, BFloat16.Zero }; + yield return new object[] { BFloat16.One, 2, BFloat16.One, BFloat16.Zero }; + yield return new object[] { BFloat16.One, 3, BFloat16.One, BFloat16.Zero }; + yield return new object[] { BFloat16.One, 4, BFloat16.One, BFloat16.Zero }; + yield return new object[] { BFloat16.One, 5, BFloat16.One, BFloat16.Zero }; + yield return new object[] { BFloat16.E, -5, (BFloat16)0.8187f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { BFloat16.E, -4, (BFloat16)0.7788f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { BFloat16.E, -3, (BFloat16)0.7165f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { BFloat16.E, -2, (BFloat16)0.6065f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { BFloat16.E, -1, (BFloat16)0.3679f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { BFloat16.E, 0, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.E, 1, BFloat16.E, BFloat16.Zero }; + yield return new object[] { BFloat16.E, 2, (BFloat16)1.6487f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { BFloat16.E, 3, (BFloat16)1.3956f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { BFloat16.E, 4, (BFloat16)1.2840f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { BFloat16.E, 5, (BFloat16)1.2214f, CrossPlatformMachineEpsilon * (BFloat16)10 }; + yield return new object[] { BFloat16.PositiveInfinity, -5, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, -4, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, -3, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, -2, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, -1, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, 0, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, 1, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, 2, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, 3, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, 4, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, 5, BFloat16.PositiveInfinity, BFloat16.Zero }; + } + + [Theory] + [MemberData(nameof(RootN_TestData))] + public static void RootN(BFloat16 x, int n, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(expectedResult, BFloat16.RootN(x, n), allowedVariance); + } + + public static IEnumerable AcosPi_TestData() + { + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.One, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)0.5391f, (BFloat16)0.3184f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)0.2051f, (BFloat16)0.4336f, CrossPlatformMachineEpsilon }; + yield return new object[] { BFloat16.Zero, (BFloat16)0.5f, BFloat16.Zero }; + yield return new object[] { -(BFloat16)0.416f, (BFloat16)0.6367f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.5703f, (BFloat16)0.6914f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.6055f, (BFloat16)0.707f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.7812f, (BFloat16)0.7852f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)1.0f, BFloat16.One, BFloat16.Zero }; + yield return new object[] { -(BFloat16)0.918f, (BFloat16)0.8711f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.2656f, (BFloat16)0.5859f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.1787f, (BFloat16)0.5586f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)0.2207f, (BFloat16)0.4297f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)0.582f, (BFloat16)0.3027f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.6328f, (BFloat16)0.7188f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.9023f, (BFloat16)0.8594f, CrossPlatformMachineEpsilon }; + } + + [Theory] + [MemberData(nameof(AcosPi_TestData))] + public static void AcosPiTest(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(expectedResult, BFloat16.AcosPi(value), allowedVariance); + } + + public static IEnumerable AsinPi_TestData() + { + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)0.8398f, (BFloat16)0.3164f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)0.9805f, (BFloat16)0.4375f, CrossPlatformMachineEpsilon }; + yield return new object[] { BFloat16.One, (BFloat16)0.5f, BFloat16.Zero }; + yield return new object[] { (BFloat16)0.9102f, (BFloat16)0.3633f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)0.8203f, (BFloat16)0.3066f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)0.7969f, (BFloat16)0.293f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)0.625f, (BFloat16)0.2148f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.3926f, -(BFloat16)0.1279f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.9648f, -(BFloat16)0.416f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.9844f, -(BFloat16)0.4434f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.9766f, -(BFloat16)0.4316f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)0.8125f, (BFloat16)0.3027f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)0.7734f, (BFloat16)0.2812f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.4297f, -(BFloat16)0.1416f, CrossPlatformMachineEpsilon }; + } + + [Theory] + [MemberData(nameof(AsinPi_TestData))] + public static void AsinPiTest(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(-expectedResult, BFloat16.AsinPi(-value), allowedVariance); + AssertEqual(+expectedResult, BFloat16.AsinPi(+value), allowedVariance); + } + + public static IEnumerable Atan2Pi_TestData() + { + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, -BFloat16.One, BFloat16.One, BFloat16.Zero }; // y: sinpi(0) x: cospi(1) + yield return new object[] { BFloat16.Zero, -BFloat16.Zero, BFloat16.One, BFloat16.Zero }; // y: sinpi(0) x: -cospi(0.5) + yield return new object[] { BFloat16.Zero, BFloat16.Zero, BFloat16.Zero, BFloat16.Zero }; // y: sinpi(0) x: cospi(0.5) + yield return new object[] { BFloat16.Zero, BFloat16.One, BFloat16.Zero, BFloat16.Zero }; // y: sinpi(0) x: cospi(0) + yield return new object[] { (BFloat16)0.8398f, (BFloat16)0.5391f, (BFloat16)0.3184f, CrossPlatformMachineEpsilon }; // y: sinpi(1 / pi) x: cospi(1 / pi) + yield return new object[] { (BFloat16)0.9805f, (BFloat16)0.2051f, (BFloat16)0.4336f, CrossPlatformMachineEpsilon }; // y: sinpi(log10(e)) x: cospi(log10(e)) + yield return new object[] { BFloat16.One, -BFloat16.Zero, (BFloat16)0.5f, BFloat16.Zero }; // y: sinpi(0.5) x: -cospi(0.5) + yield return new object[] { BFloat16.One, BFloat16.Zero, (BFloat16)0.5f, BFloat16.Zero }; // y: sinpi(0.5) x: cospi(0.5) + yield return new object[] { (BFloat16)0.9102f, -(BFloat16)0.416f, (BFloat16)0.6367f, CrossPlatformMachineEpsilon }; // y: sinpi(2 / pi) x: cospi(2 / pi) + yield return new object[] { (BFloat16)0.8203f, -(BFloat16)0.5703f, (BFloat16)0.6953f, CrossPlatformMachineEpsilon }; // y: sinpi(ln(2)) x: cospi(ln(2)) + yield return new object[] { (BFloat16)0.7969f, -(BFloat16)0.6055f, (BFloat16)0.707f, CrossPlatformMachineEpsilon }; // y: sinpi(1 / sqrt(2)) x: cospi(1 / sqrt(2)) + yield return new object[] { (BFloat16)0.625f, -(BFloat16)0.7812f, (BFloat16)0.7852f, CrossPlatformMachineEpsilon }; // y: sinpi(pi / 4) x: cospi(pi / 4) + yield return new object[] { -(BFloat16)0.3926f, -(BFloat16)0.918f, -(BFloat16)0.8711f, CrossPlatformMachineEpsilon }; // y: sinpi(2 / sqrt(pi)) x: cospi(2 / sqrt(pi)) + yield return new object[] { -(BFloat16)0.9648f, -(BFloat16)0.2656f, -(BFloat16)0.5859f, CrossPlatformMachineEpsilon }; // y: sinpi(sqrt(2)) x: cospi(sqrt(2)) + yield return new object[] { -(BFloat16)0.9844f, -(BFloat16)0.1787f, -(BFloat16)0.5586f, CrossPlatformMachineEpsilon }; // y: sinpi(log2(e)) x: cospi(log2(e)) + yield return new object[] { -(BFloat16)0.9766f, (BFloat16)0.2207f, -(BFloat16)0.4297f, CrossPlatformMachineEpsilon }; // y: sinpi(pi / 2) x: cospi(pi / 2) + yield return new object[] { (BFloat16)0.8125f, (BFloat16)0.582f, (BFloat16)0.3027f, CrossPlatformMachineEpsilon }; // y: sinpi(ln(10)) x: cospi(ln(10)) + yield return new object[] { (BFloat16)0.7734f, -(BFloat16)0.6328f, (BFloat16)0.7188f, CrossPlatformMachineEpsilon }; // y: sinpi(e) x: cospi(e) + yield return new object[] { -(BFloat16)0.4297f, -(BFloat16)0.9023f, -(BFloat16)0.8594f, CrossPlatformMachineEpsilon }; // y: sinpi(pi) x: cospi(pi) + yield return new object[] { BFloat16.One, BFloat16.NegativeInfinity, BFloat16.One, BFloat16.Zero }; // y: sinpi(0.5) + yield return new object[] { BFloat16.One, BFloat16.PositiveInfinity, BFloat16.Zero, BFloat16.Zero }; // y: sinpi(0.5) + yield return new object[] { BFloat16.PositiveInfinity, -BFloat16.One, (BFloat16)0.5f, BFloat16.Zero }; // x: cospi(1) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.One, (BFloat16)0.5f, BFloat16.Zero }; // x: cospi(0) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NegativeInfinity, (BFloat16)0.75f, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, (BFloat16)0.25f, BFloat16.Zero }; + } + + [Theory] + [MemberData(nameof(Atan2Pi_TestData))] + public static void Atan2PiTest(BFloat16 y, BFloat16 x, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(-expectedResult, BFloat16.Atan2Pi(-y, +x), allowedVariance); + AssertEqual(+expectedResult, BFloat16.Atan2Pi(+y, +x), allowedVariance); + } + + public static IEnumerable AtanPi_TestData() + { + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)1.555f, (BFloat16)0.3184f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)4.781f, (BFloat16)0.4336f, CrossPlatformMachineEpsilon }; + yield return new object[] { BFloat16.PositiveInfinity, (BFloat16)0.5f, BFloat16.Zero }; + yield return new object[] { -(BFloat16)2.188f, -(BFloat16)0.3633f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)1.438f, -(BFloat16)0.3066f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)1.312f, -(BFloat16)0.293f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)0.8008f, -(BFloat16)0.2148f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)0.4258f, (BFloat16)0.1279f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)3.625f, (BFloat16)0.4141f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)5.5f, (BFloat16)0.4434f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)4.406f, -(BFloat16)0.4297f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)1.398f, (BFloat16)0.3027f, CrossPlatformMachineEpsilon }; + yield return new object[] { -(BFloat16)1.219f, -(BFloat16)0.2812f, CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)0.4766f, (BFloat16)0.1416f, CrossPlatformMachineEpsilon }; + } + + [Theory] + [MemberData(nameof(AtanPi_TestData))] + public static void AtanPiTest(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(-expectedResult, BFloat16.AtanPi(-value), allowedVariance); + AssertEqual(+expectedResult, BFloat16.AtanPi(+value), allowedVariance); + } + + public static IEnumerable CosPi_TestData() + { + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, BFloat16.One, BFloat16.Zero }; + yield return new object[] { (BFloat16)0.3184f, (BFloat16)0.5391f, CrossPlatformMachineEpsilon }; // value: (1 / pi) + yield return new object[] { (BFloat16)0.4336f, (BFloat16)0.207f, CrossPlatformMachineEpsilon }; // value: (log10(e)) + yield return new object[] { (BFloat16)0.5f, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)0.6367f, -(BFloat16)0.416f, CrossPlatformMachineEpsilon }; // value: (2 / pi) + yield return new object[] { (BFloat16)0.6914f, -(BFloat16)0.5664f, CrossPlatformMachineEpsilon }; // value: (ln(2)) + yield return new object[] { (BFloat16)0.707f, -(BFloat16)0.6055f, CrossPlatformMachineEpsilon }; // value: (1 / sqrt(2)) + yield return new object[] { (BFloat16)0.7852f, -(BFloat16)0.7812f, CrossPlatformMachineEpsilon }; // value: (pi / 4) + yield return new object[] { BFloat16.One, -(BFloat16)1.0f, BFloat16.Zero }; + yield return new object[] { (BFloat16)1.125f, -(BFloat16)0.9258f, CrossPlatformMachineEpsilon }; // value: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)1.414f, -(BFloat16)0.2676f, CrossPlatformMachineEpsilon }; // value: (sqrt(2)) + yield return new object[] { (BFloat16)1.445f, -(BFloat16)0.1709f, CrossPlatformMachineEpsilon }; // value: (log2(e)) + yield return new object[] { (BFloat16)1.5f, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)1.57f, (BFloat16)0.2188f, CrossPlatformMachineEpsilon }; // value: (pi / 2) + yield return new object[] { (BFloat16)2.0f, (BFloat16)1.0, BFloat16.Zero }; + yield return new object[] { (BFloat16)2.297f, (BFloat16)0.5938f, CrossPlatformMachineEpsilon }; // value: (ln(10)) + yield return new object[] { (BFloat16)2.5f, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)2.719f, -(BFloat16)0.6328f, CrossPlatformMachineEpsilon }; // value: (e) + yield return new object[] { (BFloat16)3.0f, -(BFloat16)1.0, BFloat16.Zero }; + yield return new object[] { (BFloat16)3.141f, -(BFloat16)0.9023f, CrossPlatformMachineEpsilon }; // value: (pi) + yield return new object[] { (BFloat16)3.5f, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NaN, BFloat16.Zero }; + } + + [Theory] + [MemberData(nameof(CosPi_TestData))] + public static void CosPiTest(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(+expectedResult, BFloat16.CosPi(-value), allowedVariance); + AssertEqual(+expectedResult, BFloat16.CosPi(+value), allowedVariance); + } + + public static IEnumerable SinPi_TestData() + { + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)0.3184f, (BFloat16)0.8398f, CrossPlatformMachineEpsilon }; // value: (1 / pi) + yield return new object[] { (BFloat16)0.4336f, (BFloat16)0.9766f, CrossPlatformMachineEpsilon }; // value: (log10(e)) + yield return new object[] { (BFloat16)0.5f, BFloat16.One, BFloat16.Zero }; + yield return new object[] { (BFloat16)0.6367f, (BFloat16)0.9102f, CrossPlatformMachineEpsilon }; // value: (2 / pi) + yield return new object[] { (BFloat16)0.6914f, (BFloat16)0.8242f, CrossPlatformMachineEpsilon }; // value: (ln(2)) + yield return new object[] { (BFloat16)0.707f, (BFloat16)0.7969f, CrossPlatformMachineEpsilon }; // value: (1 / sqrt(2)) + yield return new object[] { (BFloat16)0.7852f, (BFloat16)0.625f, CrossPlatformMachineEpsilon }; // value: (pi / 4) + yield return new object[] { BFloat16.One, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)1.125f, -(BFloat16)0.3828f, CrossPlatformMachineEpsilon }; // value: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)1.414f, -(BFloat16)0.9648f, CrossPlatformMachineEpsilon }; // value: (sqrt(2)) + yield return new object[] { (BFloat16)1.445f, -(BFloat16)0.9844f, CrossPlatformMachineEpsilon }; // value: (log2(e)) + yield return new object[] { (BFloat16)1.5f, -(BFloat16)1f, BFloat16.Zero }; + yield return new object[] { (BFloat16)1.57f, -(BFloat16)0.9766f, CrossPlatformMachineEpsilon }; // value: (pi / 2) + yield return new object[] { (BFloat16)2.0f, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)2.297f, (BFloat16)0.8047f, CrossPlatformMachineEpsilon }; // value: (ln(10)) + yield return new object[] { (BFloat16)2.5f, BFloat16.One, BFloat16.Zero }; + yield return new object[] { (BFloat16)2.719f, (BFloat16)0.7734f, CrossPlatformMachineEpsilon }; // value: (e) + yield return new object[] { (BFloat16)3.0f, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)3.141f, -(BFloat16)0.4277f, CrossPlatformMachineEpsilon }; // value: (pi) + yield return new object[] { (BFloat16)3.5f, -(BFloat16)1f, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NaN, BFloat16.Zero }; + } + + [Theory] + [MemberData(nameof(SinPi_TestData))] + public static void SinPiTest(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(-expectedResult, BFloat16.SinPi(-value), allowedVariance); + AssertEqual(+expectedResult, BFloat16.SinPi(+value), allowedVariance); + } + + public static IEnumerable TanPi_TestData() + { + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)0.3184f, (BFloat16)1.555f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (1 / pi) + yield return new object[] { (BFloat16)0.4336f, (BFloat16)4.719f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (log10(e)) + yield return new object[] { (BFloat16)0.5f, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { (BFloat16)0.6367f, -(BFloat16)2.188f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (2 / pi) + yield return new object[] { (BFloat16)0.6914f, -(BFloat16)1.461f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (ln(2)) + yield return new object[] { (BFloat16)0.707f, -(BFloat16)1.312f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (1 / sqrt(2)) + yield return new object[] { (BFloat16)0.7852f, -(BFloat16)0.8008f, CrossPlatformMachineEpsilon }; // value: (pi / 4) + yield return new object[] { BFloat16.One, -BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)1.125f, (BFloat16)0.4141f, CrossPlatformMachineEpsilon }; // value: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)1.414f, (BFloat16)3.609f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (sqrt(2)) + yield return new object[] { (BFloat16)1.445f, (BFloat16)5.75f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (log2(e)) + yield return new object[] { (BFloat16)1.5f, BFloat16.NegativeInfinity, BFloat16.Zero }; + yield return new object[] { (BFloat16)1.57f, -(BFloat16)4.469f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (pi / 2) + yield return new object[] { (BFloat16)2.0f, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)2.297f, (BFloat16)1.352f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (ln(10)) + yield return new object[] { (BFloat16)2.5f, BFloat16.PositiveInfinity, BFloat16.Zero }; + yield return new object[] { (BFloat16)2.719f, -(BFloat16)1.219f, CrossPlatformMachineEpsilon * (BFloat16)10 }; // value: (e) + yield return new object[] { (BFloat16)3.0f, -BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)3.141f, (BFloat16)0.4727f, CrossPlatformMachineEpsilon }; // value: (pi) + yield return new object[] { (BFloat16)3.5f, BFloat16.NegativeInfinity, BFloat16.Zero }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NaN, BFloat16.Zero }; + } + + [Theory] + [MemberData(nameof(TanPi_TestData))] + public static void TanPiTest(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(-expectedResult, BFloat16.TanPi(-value), allowedVariance); + AssertEqual(+expectedResult, BFloat16.TanPi(+value), allowedVariance); + } + + public static IEnumerable BitDecrement_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.NegativeInfinity }; + yield return new object[] { UInt16BitsToBFloat16(0xC049), UInt16BitsToBFloat16(0xC04A) }; // value: -(pi) + yield return new object[] { UInt16BitsToBFloat16(0xC02E), UInt16BitsToBFloat16(0xC02F) }; // value: -(e) + yield return new object[] { UInt16BitsToBFloat16(0xC013), UInt16BitsToBFloat16(0xC014) }; // value: -(ln(10)) + yield return new object[] { UInt16BitsToBFloat16(0xBFC9), UInt16BitsToBFloat16(0xBFCA) }; // value: -(pi / 2) + yield return new object[] { UInt16BitsToBFloat16(0xBFB9), UInt16BitsToBFloat16(0xBFBA) }; // value: -(log2(e)) + yield return new object[] { UInt16BitsToBFloat16(0xBFB5), UInt16BitsToBFloat16(0xBFB6) }; // value: -(sqrt(2)) + yield return new object[] { UInt16BitsToBFloat16(0xBF90), UInt16BitsToBFloat16(0xBF91) }; // value: -(2 / sqrt(pi)) + yield return new object[] { UInt16BitsToBFloat16(0xBF80), UInt16BitsToBFloat16(0xBF81) }; + yield return new object[] { UInt16BitsToBFloat16(0xBF49), UInt16BitsToBFloat16(0xBF4A) }; // value: -(pi / 4) + yield return new object[] { UInt16BitsToBFloat16(0xBF35), UInt16BitsToBFloat16(0xBF36) }; // value: -(1 / sqrt(2)) + yield return new object[] { UInt16BitsToBFloat16(0xBF31), UInt16BitsToBFloat16(0xBF32) }; // value: -(ln(2)) + yield return new object[] { UInt16BitsToBFloat16(0xBF23), UInt16BitsToBFloat16(0xBF24) }; // value: -(2 / pi) + yield return new object[] { UInt16BitsToBFloat16(0xBEDE), UInt16BitsToBFloat16(0xBEDF) }; // value: -(log10(e)) + yield return new object[] { UInt16BitsToBFloat16(0xBEA3), UInt16BitsToBFloat16(0xBEA4) }; // value: -(1 / pi) + yield return new object[] { UInt16BitsToBFloat16(0x8000), -BFloat16.Epsilon }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN }; + yield return new object[] { UInt16BitsToBFloat16(0x0000), -BFloat16.Epsilon }; + yield return new object[] { UInt16BitsToBFloat16(0x3EA3), UInt16BitsToBFloat16(0x3EA2) }; // value: (1 / pi) + yield return new object[] { UInt16BitsToBFloat16(0x3EDE), UInt16BitsToBFloat16(0x3EDD) }; // value: (log10(e)) + yield return new object[] { UInt16BitsToBFloat16(0x3F23), UInt16BitsToBFloat16(0x3F22) }; // value: (2 / pi) + yield return new object[] { UInt16BitsToBFloat16(0x3F31), UInt16BitsToBFloat16(0x3F30) }; // value: (ln(2)) + yield return new object[] { UInt16BitsToBFloat16(0x3F35), UInt16BitsToBFloat16(0x3F34) }; // value: (1 / sqrt(2)) + yield return new object[] { UInt16BitsToBFloat16(0x3F49), UInt16BitsToBFloat16(0x3F48) }; // value: (pi / 4) + yield return new object[] { UInt16BitsToBFloat16(0x3F80), UInt16BitsToBFloat16(0x3F7F) }; + yield return new object[] { UInt16BitsToBFloat16(0x3F90), UInt16BitsToBFloat16(0x3F8F) }; // value: (2 / sqrt(pi)) + yield return new object[] { UInt16BitsToBFloat16(0x3FB5), UInt16BitsToBFloat16(0x3FB4) }; // value: (sqrt(2)) + yield return new object[] { UInt16BitsToBFloat16(0x3FB9), UInt16BitsToBFloat16(0x3FB8) }; // value: (log2(e)) + yield return new object[] { UInt16BitsToBFloat16(0x3FC9), UInt16BitsToBFloat16(0x3FC8) }; // value: (pi / 2) + yield return new object[] { UInt16BitsToBFloat16(0x4013), UInt16BitsToBFloat16(0x4012) }; // value: (ln(10)) + yield return new object[] { UInt16BitsToBFloat16(0x402E), UInt16BitsToBFloat16(0x402D) }; // value: (e) + yield return new object[] { UInt16BitsToBFloat16(0x4049), UInt16BitsToBFloat16(0x4048) }; // value: (pi) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.MaxValue }; + } + + [Theory] + [MemberData(nameof(BitDecrement_TestData))] + public static void BitDecrement(BFloat16 value, BFloat16 expectedResult) + { + AssertEqual(expectedResult, BFloat16.BitDecrement(value), BFloat16.Zero); + } + + public static IEnumerable BitIncrement_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.MinValue }; + yield return new object[] { UInt16BitsToBFloat16(0xC049), UInt16BitsToBFloat16(0xC048) }; // value: -(pi) + yield return new object[] { UInt16BitsToBFloat16(0xC02E), UInt16BitsToBFloat16(0xC02D) }; // value: -(e) + yield return new object[] { UInt16BitsToBFloat16(0xC013), UInt16BitsToBFloat16(0xC012) }; // value: -(ln(10)) + yield return new object[] { UInt16BitsToBFloat16(0xBFC9), UInt16BitsToBFloat16(0xBFC8) }; // value: -(pi / 2) + yield return new object[] { UInt16BitsToBFloat16(0xBFB9), UInt16BitsToBFloat16(0xBFB8) }; // value: -(log2(e)) + yield return new object[] { UInt16BitsToBFloat16(0xBFB5), UInt16BitsToBFloat16(0xBFB4) }; // value: -(sqrt(2)) + yield return new object[] { UInt16BitsToBFloat16(0xBF90), UInt16BitsToBFloat16(0xBF8F) }; // value: -(2 / sqrt(pi)) + yield return new object[] { UInt16BitsToBFloat16(0xBF80), UInt16BitsToBFloat16(0xBF7F) }; + yield return new object[] { UInt16BitsToBFloat16(0xBF49), UInt16BitsToBFloat16(0xBF48) }; // value: -(pi / 4) + yield return new object[] { UInt16BitsToBFloat16(0xBF35), UInt16BitsToBFloat16(0xBF34) }; // value: -(1 / sqrt(2)) + yield return new object[] { UInt16BitsToBFloat16(0xBF31), UInt16BitsToBFloat16(0xBF30) }; // value: -(ln(2)) + yield return new object[] { UInt16BitsToBFloat16(0xBF23), UInt16BitsToBFloat16(0xBF22) }; // value: -(2 / pi) + yield return new object[] { UInt16BitsToBFloat16(0xBEDE), UInt16BitsToBFloat16(0xBEDD) }; // value: -(log10(e)) + yield return new object[] { UInt16BitsToBFloat16(0xBEA3), UInt16BitsToBFloat16(0xBEA2) }; // value: -(1 / pi) + yield return new object[] { UInt16BitsToBFloat16(0x8000), BFloat16.Epsilon }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN }; + yield return new object[] { UInt16BitsToBFloat16(0x0000), BFloat16.Epsilon }; + yield return new object[] { UInt16BitsToBFloat16(0x3EA3), UInt16BitsToBFloat16(0x3EA4) }; // value: (1 / pi) + yield return new object[] { UInt16BitsToBFloat16(0x3EDE), UInt16BitsToBFloat16(0x3EDF) }; // value: (log10(e)) + yield return new object[] { UInt16BitsToBFloat16(0x3F23), UInt16BitsToBFloat16(0x3F24) }; // value: (2 / pi) + yield return new object[] { UInt16BitsToBFloat16(0x3F31), UInt16BitsToBFloat16(0x3F32) }; // value: (ln(2)) + yield return new object[] { UInt16BitsToBFloat16(0x3F35), UInt16BitsToBFloat16(0x3F36) }; // value: (1 / sqrt(2)) + yield return new object[] { UInt16BitsToBFloat16(0x3F49), UInt16BitsToBFloat16(0x3F4A) }; // value: (pi / 4) + yield return new object[] { UInt16BitsToBFloat16(0x3F80), UInt16BitsToBFloat16(0x3F81) }; + yield return new object[] { UInt16BitsToBFloat16(0x3F90), UInt16BitsToBFloat16(0x3F91) }; // value: (2 / sqrt(pi)) + yield return new object[] { UInt16BitsToBFloat16(0x3FB5), UInt16BitsToBFloat16(0x3FB6) }; // value: (sqrt(2)) + yield return new object[] { UInt16BitsToBFloat16(0x3FB9), UInt16BitsToBFloat16(0x3FBA) }; // value: (log2(e)) + yield return new object[] { UInt16BitsToBFloat16(0x3FC9), UInt16BitsToBFloat16(0x3FCA) }; // value: (pi / 2) + yield return new object[] { UInt16BitsToBFloat16(0x4013), UInt16BitsToBFloat16(0x4014) }; // value: (ln(10)) + yield return new object[] { UInt16BitsToBFloat16(0x402E), UInt16BitsToBFloat16(0x402F) }; // value: (e) + yield return new object[] { UInt16BitsToBFloat16(0x4049), UInt16BitsToBFloat16(0x404A) }; // value: (pi) + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity }; + } + + [Theory] + [MemberData(nameof(BitIncrement_TestData))] + public static void BitIncrement(BFloat16 value, BFloat16 expectedResult) + { + AssertEqual(expectedResult, BFloat16.BitIncrement(value), BFloat16.Zero); + } + + public static IEnumerable Lerp_TestData() + { + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.NegativeInfinity, (BFloat16)(0.5f), BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.NaN, (BFloat16)(0.5f), BFloat16.NaN }; + yield return new object[] { BFloat16.NegativeInfinity, BFloat16.PositiveInfinity, (BFloat16)(0.5f), BFloat16.NaN }; + yield return new object[] { BFloat16.NegativeInfinity, (BFloat16)(0.0f), (BFloat16)(0.5f), BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.NegativeInfinity, (BFloat16)(1.0f), (BFloat16)(0.5f), BFloat16.NegativeInfinity }; + yield return new object[] { BFloat16.NaN, BFloat16.NegativeInfinity, (BFloat16)(0.5f), BFloat16.NaN }; + yield return new object[] { BFloat16.NaN, BFloat16.NaN, (BFloat16)(0.5f), BFloat16.NaN }; + yield return new object[] { BFloat16.NaN, BFloat16.PositiveInfinity, (BFloat16)(0.5f), BFloat16.NaN }; + yield return new object[] { BFloat16.NaN, (BFloat16)(0.0f), (BFloat16)(0.5f), BFloat16.NaN }; + yield return new object[] { BFloat16.NaN, (BFloat16)(1.0f), (BFloat16)(0.5f), BFloat16.NaN }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NegativeInfinity, (BFloat16)(0.5f), BFloat16.NaN }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.NaN, (BFloat16)(0.5f), BFloat16.NaN }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, (BFloat16)(0.5f), BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.PositiveInfinity, (BFloat16)(0.0f), (BFloat16)(0.5f), BFloat16.PositiveInfinity }; + yield return new object[] { BFloat16.PositiveInfinity, (BFloat16)(1.0f), (BFloat16)(0.5f), BFloat16.PositiveInfinity }; + yield return new object[] { (BFloat16)(1.0f), (BFloat16)(3.0f), (BFloat16)(0.0f), (BFloat16)(1.0f) }; + yield return new object[] { (BFloat16)(1.0f), (BFloat16)(3.0f), (BFloat16)(0.5f), (BFloat16)(2.0f) }; + yield return new object[] { (BFloat16)(1.0f), (BFloat16)(3.0f), (BFloat16)(1.0f), (BFloat16)(3.0f) }; + yield return new object[] { (BFloat16)(1.0f), (BFloat16)(3.0f), (BFloat16)(2.0f), (BFloat16)(5.0f) }; + yield return new object[] { (BFloat16)(2.0f), (BFloat16)(4.0f), (BFloat16)(0.0f), (BFloat16)(2.0f) }; + yield return new object[] { (BFloat16)(2.0f), (BFloat16)(4.0f), (BFloat16)(0.5f), (BFloat16)(3.0f) }; + yield return new object[] { (BFloat16)(2.0f), (BFloat16)(4.0f), (BFloat16)(1.0f), (BFloat16)(4.0f) }; + yield return new object[] { (BFloat16)(2.0f), (BFloat16)(4.0f), (BFloat16)(2.0f), (BFloat16)(6.0f) }; + yield return new object[] { (BFloat16)(3.0f), (BFloat16)(1.0f), (BFloat16)(0.0f), (BFloat16)(3.0f) }; + yield return new object[] { (BFloat16)(3.0f), (BFloat16)(1.0f), (BFloat16)(0.5f), (BFloat16)(2.0f) }; + yield return new object[] { (BFloat16)(3.0f), (BFloat16)(1.0f), (BFloat16)(1.0f), (BFloat16)(1.0f) }; + yield return new object[] { (BFloat16)(3.0f), (BFloat16)(1.0f), (BFloat16)(2.0f), -(BFloat16)(1.0f) }; + yield return new object[] { (BFloat16)(4.0f), (BFloat16)(2.0f), (BFloat16)(0.0f), (BFloat16)(4.0f) }; + yield return new object[] { (BFloat16)(4.0f), (BFloat16)(2.0f), (BFloat16)(0.5f), (BFloat16)(3.0f) }; + yield return new object[] { (BFloat16)(4.0f), (BFloat16)(2.0f), (BFloat16)(1.0f), (BFloat16)(2.0f) }; + yield return new object[] { (BFloat16)(4.0f), (BFloat16)(2.0f), (BFloat16)(2.0f), (BFloat16)(0.0f) }; + } + + [Theory] + [MemberData(nameof(Lerp_TestData))] + public static void LerpTest(BFloat16 value1, BFloat16 value2, BFloat16 amount, BFloat16 expectedResult) + { + AssertEqual(+expectedResult, BFloat16.Lerp(+value1, +value2, amount), BFloat16.Zero); + AssertEqual((expectedResult == BFloat16.Zero) ? expectedResult : -expectedResult, BFloat16.Lerp(-value1, -value2, amount), BFloat16.Zero); + } + + public static IEnumerable DegreesToRadians_TestData() + { + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)(0.3184f), (BFloat16)(0.005554f), CrossPlatformMachineEpsilon }; // value: (1 / pi) + yield return new object[] { (BFloat16)(0.4336f), (BFloat16)(0.007568f), CrossPlatformMachineEpsilon }; // value: (log10(e)) + yield return new object[] { (BFloat16)(0.5f), (BFloat16)(0.008728f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(0.6367f), (BFloat16)(0.01111f), CrossPlatformMachineEpsilon }; // value: (2 / pi) + yield return new object[] { (BFloat16)(0.6953f), (BFloat16)(0.01215f), CrossPlatformMachineEpsilon }; // value: (ln(2)) + yield return new object[] { (BFloat16)(0.707f), (BFloat16)(0.01233f), CrossPlatformMachineEpsilon }; // value: (1 / sqrt(2)) + yield return new object[] { (BFloat16)(0.7852f), (BFloat16)(0.01373f), CrossPlatformMachineEpsilon }; // value: (pi / 4) + yield return new object[] { (BFloat16)(1.0f), (BFloat16)(0.01746f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(1.125f), (BFloat16)(0.01965f), CrossPlatformMachineEpsilon }; // value: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)(1.414f), (BFloat16)(0.02466f), CrossPlatformMachineEpsilon }; // value: (sqrt(2)) + yield return new object[] { (BFloat16)(1.445f), (BFloat16)(0.02527f), CrossPlatformMachineEpsilon }; // value: (log2(e)) + yield return new object[] { (BFloat16)(1.5f), (BFloat16)(0.02612f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(1.57f), (BFloat16)(0.02747f), CrossPlatformMachineEpsilon }; // value: (pi / 2) + yield return new object[] { (BFloat16)(2.0f), (BFloat16)(0.03491f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(2.297f), (BFloat16)(0.04004f), CrossPlatformMachineEpsilon }; // value: (ln(10)) + yield return new object[] { (BFloat16)(2.5f), (BFloat16)(0.0437f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(2.719f), (BFloat16)(0.04736f), CrossPlatformMachineEpsilon }; // value: (e) + yield return new object[] { (BFloat16)(3.0f), (BFloat16)(0.05225f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(3.141f), (BFloat16)(0.05493f), CrossPlatformMachineEpsilon }; // value: (pi) + yield return new object[] { (BFloat16)(3.5f), (BFloat16)(0.06104f), CrossPlatformMachineEpsilon }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, BFloat16.Zero }; + } + + [Theory] + [MemberData(nameof(DegreesToRadians_TestData))] + public static void DegreesToRadiansTest(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(-expectedResult, BFloat16.DegreesToRadians(-value), allowedVariance); + AssertEqual(+expectedResult, BFloat16.DegreesToRadians(+value), allowedVariance); + } + + public static IEnumerable RadiansToDegrees_TestData() + { + yield return new object[] { BFloat16.NaN, BFloat16.NaN, BFloat16.Zero }; + yield return new object[] { BFloat16.Zero, BFloat16.Zero, BFloat16.Zero }; + yield return new object[] { (BFloat16)(0.005554f), (BFloat16)(0.3184f), CrossPlatformMachineEpsilon }; // value: (1 / pi) + yield return new object[] { (BFloat16)(0.007568f), (BFloat16)(0.4336f), CrossPlatformMachineEpsilon }; // value: (log10(e)) + yield return new object[] { (BFloat16)(0.008728f), (BFloat16)(0.5f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(0.01111f), (BFloat16)(0.6367f), CrossPlatformMachineEpsilon }; // value: (2 / pi) + yield return new object[] { (BFloat16)(0.01208f), (BFloat16)(0.6914f), CrossPlatformMachineEpsilon }; // value: (ln(2)) + yield return new object[] { (BFloat16)(0.01233f), (BFloat16)(0.707f), CrossPlatformMachineEpsilon }; // value: (1 / sqrt(2)) + yield return new object[] { (BFloat16)(0.01367f), (BFloat16)(0.7852f), CrossPlatformMachineEpsilon }; // value: (pi / 4) + yield return new object[] { (BFloat16)(0.01746f), (BFloat16)(1f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(0.01965f), (BFloat16)(1.125f), CrossPlatformMachineEpsilon }; // value: (2 / sqrt(pi)) + yield return new object[] { (BFloat16)(0.02466f), (BFloat16)(1.414f), CrossPlatformMachineEpsilon }; // value: (sqrt(2)) + yield return new object[] { (BFloat16)(0.02515f), (BFloat16)(1.438f), CrossPlatformMachineEpsilon }; // value: (log2(e)) + yield return new object[] { (BFloat16)(0.02612f), (BFloat16)(1.5f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(0.02734f), (BFloat16)(1.57f), CrossPlatformMachineEpsilon }; // value: (pi / 2) + yield return new object[] { (BFloat16)(0.03491f), (BFloat16)(2f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(0.04004f), (BFloat16)(2.297f), CrossPlatformMachineEpsilon }; // value: (ln(10)) + yield return new object[] { (BFloat16)(0.0437f), (BFloat16)(2.5f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(0.04736f), (BFloat16)(2.719f), CrossPlatformMachineEpsilon }; // value: (e) + yield return new object[] { (BFloat16)(0.05225f), (BFloat16)(3f), CrossPlatformMachineEpsilon }; + yield return new object[] { (BFloat16)(0.05469f), (BFloat16)(3.141f), CrossPlatformMachineEpsilon }; // value: (pi) + yield return new object[] { (BFloat16)(0.06104f), (BFloat16)(3.5f), CrossPlatformMachineEpsilon }; + yield return new object[] { BFloat16.PositiveInfinity, BFloat16.PositiveInfinity, BFloat16.Zero }; + } + + [Theory] + [MemberData(nameof(RadiansToDegrees_TestData))] + public static void RadiansToDegreesTest(BFloat16 value, BFloat16 expectedResult, BFloat16 allowedVariance) + { + AssertEqual(-expectedResult, BFloat16.RadiansToDegrees(-value), allowedVariance); + AssertEqual(+expectedResult, BFloat16.RadiansToDegrees(+value), allowedVariance); + } + + #region AssertExtentions + static bool IsNegativeZero(BFloat16 value) + { + return BFloat16ToUInt16Bits(value) == 0x8000; + } + + static bool IsPositiveZero(BFloat16 value) + { + return BFloat16ToUInt16Bits(value) == 0; + } + + static string ToStringPadded(BFloat16 value) + { + if (BFloat16.IsNaN(value)) + { + return "NaN".PadLeft(5); + } + else if (BFloat16.IsPositiveInfinity(value)) + { + return "+\u221E".PadLeft(5); + } + else if (BFloat16.IsNegativeInfinity(value)) + { + return "-\u221E".PadLeft(5); + } + else if (IsNegativeZero(value)) + { + return "-0.0".PadLeft(5); + } + else if (IsPositiveZero(value)) + { + return "+0.0".PadLeft(5); + } + else + { + return $"{value,5:G5}"; + } + } + + /// Verifies that two values's binary representations are identical. + /// The expected value + /// The value to be compared against + /// Thrown when the representations are not identical + private static void AssertEqual(BFloat16 expected, BFloat16 actual) + { + if (BFloat16ToUInt16Bits(expected) == BFloat16ToUInt16Bits(actual)) + { + return; + } + + if (PlatformDetection.IsRiscV64Process && BFloat16.IsNaN(expected) && BFloat16.IsNaN(actual)) + { + // RISC-V does not preserve payload + return; + } + + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + + /// Verifies that two values are equal, within the . + /// The expected value + /// The value to be compared against + /// The total variance allowed between the expected and actual results. + /// Thrown when the values are not equal + private static void AssertEqual(BFloat16 expected, BFloat16 actual, BFloat16 variance) + { + if (BFloat16.IsNaN(expected)) + { + if (BFloat16.IsNaN(actual)) + { + return; + } + + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (BFloat16.IsNaN(actual)) + { + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (BFloat16.IsNegativeInfinity(expected)) + { + if (BFloat16.IsNegativeInfinity(actual)) + { + return; + } + + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (BFloat16.IsNegativeInfinity(actual)) + { + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (BFloat16.IsPositiveInfinity(expected)) + { + if (BFloat16.IsPositiveInfinity(actual)) + { + return; + } + + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (BFloat16.IsPositiveInfinity(actual)) + { + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (IsNegativeZero(expected)) + { + if (IsNegativeZero(actual)) + { + return; + } + + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly -0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + else if (IsNegativeZero(actual)) + { + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly -0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + + if (IsPositiveZero(expected)) + { + if (IsPositiveZero(actual)) + { + return; + } + + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly +0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + else if (IsPositiveZero(actual)) + { + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly +0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + + BFloat16 delta = (BFloat16)Math.Abs((float)actual - (float)expected); + + if (delta > variance) + { + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + } + #endregion + } +}