| File: Utilities\FloatUtils.cs | Web Access |
| Project: src\src\Microsoft.ML.Core\Microsoft.ML.Core.csproj (Microsoft.ML.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Globalization; using System.Runtime.InteropServices; using Microsoft.ML.Runtime; namespace Microsoft.ML.Internal.Utilities { [BestFriend] internal static class FloatUtils { // This is used to read and write the bits of a Double. // Thanks to Vance Morrison for educating me about this excellent aliasing mechanism. [StructLayout(LayoutKind.Explicit)] private struct DoubleBits { // Masks for the portions of a Double: 1 sign bit, 11 exponent bits, 52 mantissa bits. public const ulong MaskSign = 0x8000000000000000UL; public const ulong MaskExp = 0x7FF0000000000000UL; public const ulong MaskMan = 0x000FFFFFFFFFFFFFUL; public const int RawExpInf = 0x7FF; // The raw exponent value for infinities and nan. public const int RawExpZero = 0x3FF; // The raw exponent value for "1", when the exponent is logically zero. public const int CbitExp = 11; // Number of exponent bits. public const int CbitMan = 52; // Number of mantissa bits. public const int ExpDenorm = -1074; public const int ExpOrigin = 1075; [FieldOffset(0)] public double Float; // used only for construction from Double [FieldOffset(0)] public ulong Bits; //overlay public int GetExp() { return ((int)(Bits >> CbitMan) & RawExpInf) - RawExpZero; } public int GetRawExp() { return (int)(Bits >> CbitMan) & RawExpInf; } public bool IsFinite() { return (Bits & MaskExp) < MaskExp; } public bool IsFiniteNonZero() { var bits = Bits & ~MaskSign; return 0 < bits && bits < MaskExp; } public bool IsFiniteNormal() { var exp = Bits & MaskExp; return 0 < exp && exp < MaskExp; } public bool IsDenormal() { return (Bits & MaskExp) == 0; } public void SetExponent(int exp) { Contracts.Assert(-RawExpZero < exp && exp < RawExpInf - RawExpZero); Bits = (Bits & ~MaskExp) | (((ulong)(exp + RawExpZero) << CbitMan) & MaskExp); } public void GetParts(out int sign, out int exp, out ulong man, out bool fFinite) { sign = 1 - ((int)(Bits >> 62) & 2); man = Bits & MaskMan; exp = GetRawExp(); if (exp == 0) { // Denormalized number. fFinite = true; if (man != 0) exp = ExpDenorm; } else if (exp == RawExpInf) { // NaN or infinite. fFinite = false; exp = int.MaxValue; } else { fFinite = true; man += MaskMan + 1; exp -= ExpOrigin; } } public void SetFromParts(int sign, int exp, ulong man) { if (man == 0) Bits = 0; else { // Normalize so that 0x0010 0000 0000 0000 is the highest bit set. int cbitShift = Utils.CbitHighZero(man) - CbitExp; if (cbitShift < 0) { // REVIEW: Should this round? man >>= -cbitShift; } else man <<= cbitShift; exp -= cbitShift; Contracts.Assert((man & ~MaskMan) == MaskMan + 1); // Move the point to just behind the leading 1: 0x001.0 0000 0000 0000 // (52 bits) and skew the exponent (by 0x3FF == 1023). exp += ExpOrigin; if (exp >= RawExpInf) { // Infinity. Bits = MaskExp; } else if (exp <= 0) { // Denormalized. exp--; if (exp < -CbitMan) { // Underflow to zero. Bits = 0; } else { Bits = man >> (int)(-exp); Contracts.Assert(Bits != 0); } } else { // Mask off the implicit high bit. Bits = (man & MaskMan) | ((ulong)exp << CbitMan); } } if (sign < 0) Bits |= MaskSign; } public void SetPowerOfTwo(int exp) { if (exp >= RawExpInf || (exp += RawExpZero) >= RawExpInf) Bits = MaskExp; // Overflow to infinity. else if (exp > 0) Bits = (ulong)exp << CbitMan; else if ((exp += CbitMan - 1) < 0) Bits = 0; // Underflow to zero. else Bits = 1UL << exp; // Denormal. } public void NormalizeExponent() { // This doesn't work on denormalized numbers or non-finite values. Contracts.Assert(IsFiniteNormal()); Bits = (Bits & ~MaskExp) | ((ulong)RawExpZero << CbitMan); } public void TruncateMantissaToSingleBit() { if (Bits == 0) return; int exp = GetRawExp(); if (exp == RawExpInf) return; if (exp != 0) { // Mantissa has an implicit high bit. Bits &= ~MaskMan; } else { // Denormalized number. // Clear all but the high bit in the mantissa. ulong tmp; ulong man = Bits & MaskMan; while ((tmp = man & (man - 1)) != 0) man = tmp; Bits = (Bits & MaskSign) | man; } } public void Truncate() { int exp = GetRawExp(); if (exp == RawExpInf) // NaN or infinite. return; exp -= ExpOrigin; if (exp >= 0) return; if (exp <= -CbitMan - 1) { // Preserve only the sign. The rest is zero. Bits &= MaskSign; } else { // Clear the low (-exp) bits. Bits &= ~Utils.UuMaskBelow(-exp); } } } // This is used to read and write the bits of a Single. // Thanks to Vance Morrison for educating me about this excellent aliasing mechanism. [StructLayout(LayoutKind.Explicit)] private struct SingleBits { // Masks for the portions of a Single: 1 sign bit, 8 exponent bits, 23 mantissa bits. public const uint MaskSign = 0x80000000U; public const uint MaskExp = 0x7F800000U; public const uint MaskMan = 0x007FFFFFU; public const int RawExpInf = 0xFF; // The raw exponent value for infinities and nan. public const int RawExpZero = 0x7F; // The raw exponent value for "1", when the exponent is logically zero. public const int CbitExp = 8; // Number of exponent bits. public const int CbitMan = 23; // Number of mantissa bits. public const int ExpDenorm = -126; public const int ExpOrigin = 127; [FieldOffset(0)] public float Float; // used only for construction from Single [FieldOffset(0)] public uint Bits; //overlay public int GetExp() { return ((int)(Bits >> CbitMan) & RawExpInf) - RawExpZero; } public int GetRawExp() { return (int)(Bits >> CbitMan) & RawExpInf; } public bool IsFinite() { return (Bits & MaskExp) < MaskExp; } public bool IsFiniteNonZero() { var bits = Bits & ~MaskSign; return 0 < bits && bits < MaskExp; } public bool IsFiniteNormal() { var exp = Bits & MaskExp; return 0 < exp && exp < MaskExp; } public bool IsDenormal() { return (Bits & MaskExp) == 0; } public void SetExponent(int exp) { Contracts.Assert(-RawExpZero < exp && exp < RawExpInf - RawExpZero); Bits = (Bits & ~MaskExp) | (((uint)(exp + RawExpZero) << CbitMan) & MaskExp); } public void GetParts(out int sign, out int exp, out uint man, out bool fFinite) { sign = 1 - ((int)(Bits >> 30) & 2); man = Bits & MaskMan; exp = GetRawExp(); if (exp == 0) { // Denormalized number. fFinite = true; if (man != 0) exp = ExpDenorm; } else if (exp == RawExpInf) { // NaN or infinite. fFinite = false; exp = int.MaxValue; } else { fFinite = true; man += MaskMan + 1; exp += ExpDenorm - 1; } } public void SetFromParts(int sign, int exp, uint man) { if (man == 0) Bits = 0; else { // Normalize so that 0x0010 0000 0000 0000 is the highest bit set. int cbitShift = Utils.CbitHighZero(man) - CbitExp; if (cbitShift < 0) { // REVIEW: Should this round? man >>= -cbitShift; } else man <<= cbitShift; exp -= cbitShift; Contracts.Assert((man & ~MaskMan) == MaskMan + 1); // Move the point to just behind the leading 1: 0x001.0 0000 0000 0000 // (52 bits) and skew the exponent (by 0x3FF == 1023). exp += ExpOrigin; if (exp >= RawExpInf) { // Infinity. Bits = MaskExp; } else if (exp <= 0) { // Denormalized. exp--; if (exp < -CbitMan) { // Underflow to zero. Bits = 0; } else { Bits = man >> (int)(-exp); Contracts.Assert(Bits != 0); } } else { // Mask off the implicit high bit. Bits = (man & MaskMan) | ((uint)exp << CbitMan); } } if (sign < 0) Bits |= MaskSign; } public void SetPowerOfTwo(int exp) { if (exp >= RawExpInf || (exp += RawExpZero) >= RawExpInf) Bits = MaskExp; // Overflow to infinity. else if (exp > 0) Bits = (uint)exp << CbitMan; else if ((exp += CbitMan - 1) < 0) Bits = 0; // Underflow to zero. else Bits = 1U << exp; // Denormal. } public void NormalizeExponent() { // This doesn't work on denormalized numbers or non-finite values. Contracts.Assert(IsFiniteNormal()); Bits = (Bits & ~MaskExp) | ((uint)RawExpZero << CbitMan); } public void TruncateMantissaToSingleBit() { if (Bits == 0) return; int exp = GetRawExp(); if (exp == RawExpInf) return; if (exp != 0) { // Mantissa has an implicit high bit. Bits &= ~MaskMan; } else { // Denormalized number. // Clear all but the high bit in the mantissa. uint tmp; uint man = Bits & MaskMan; while ((tmp = man & (man - 1)) != 0) man = tmp; Bits = (Bits & MaskSign) | man; } } public void Truncate() { int exp = GetRawExp(); if (exp == RawExpInf) // NaN or infinite. return; exp -= ExpOrigin; if (exp >= 0) return; if (exp <= -CbitMan - 1) { // Preserve only the sign. The rest is zero. Bits &= MaskSign; } else { // Clear the low (-exp) bits. Bits &= ~Utils.UMaskBelow(-exp); } } } public static ulong GetBits(double x) { var bits = default(DoubleBits); bits.Float = x; return bits.Bits; } public static uint GetBits(float x) { var bits = default(SingleBits); bits.Float = x; return bits.Bits; } public static double FromBits(ulong bits) { var sb = default(DoubleBits); sb.Bits = bits; return sb.Float; } public static float FromBits(uint bits) { var sb = default(SingleBits); sb.Bits = bits; return sb.Float; } public static bool IsFinite(double x) { var bits = default(DoubleBits); bits.Float = x; return bits.IsFinite(); } public static bool IsFinite(float x) { var bits = default(SingleBits); bits.Float = x; return bits.IsFinite(); } public static bool IsFinite(ReadOnlySpan<double> values) { // Assuming that non-finites are rare, this is faster than testing on each item. double sum = 0; for (int i = 0; i < values.Length; i++) { var v = values[i]; sum += v - v; } return sum == 0; } // REVIEW: Consider implementing using SSE. public static bool IsFinite(ReadOnlySpan<float> values) { // Assuming that non-finites are rare, this is faster than testing on each item. float sum = 0; for (int i = 0; i < values.Length; i++) { var v = values[i]; sum += v - v; } return sum == 0; } public static bool IsFiniteNonZero(double x) { var bits = default(DoubleBits); bits.Float = x; return bits.IsFiniteNonZero(); } public static bool IsFiniteNonZero(float x) { var bits = default(SingleBits); bits.Float = x; return bits.IsFiniteNonZero(); } public static bool IsFiniteNormal(double x) { var bits = default(DoubleBits); bits.Float = x; return bits.IsFiniteNormal(); } public static bool IsFiniteNormal(float x) { var bits = default(SingleBits); bits.Float = x; return bits.IsFiniteNormal(); } public static bool IsDenormal(double x) { var bits = default(DoubleBits); bits.Float = x; return bits.IsDenormal(); } public static bool IsDenormal(float x) { var bits = default(SingleBits); bits.Float = x; return bits.IsDenormal(); } public static int GetExponent(double x) { var bits = default(DoubleBits); bits.Float = x; return bits.GetExp(); } public static int GetExponent(float x) { var bits = default(SingleBits); bits.Float = x; return bits.GetExp(); } public static double SetExponent(double x, int exp) { var bits = default(DoubleBits); bits.Float = x; bits.SetExponent(exp); return bits.Float; } public static float SetExponent(float x, int exp) { var bits = default(SingleBits); bits.Float = x; bits.SetExponent(exp); return bits.Float; } public static void GetParts(double x, out int sign, out int exp, out ulong man, out bool fFinite) { var bits = default(DoubleBits); bits.Float = x; bits.GetParts(out sign, out exp, out man, out fFinite); } public static void GetParts(float x, out int sign, out int exp, out uint man, out bool fFinite) { var bits = default(SingleBits); bits.Float = x; bits.GetParts(out sign, out exp, out man, out fFinite); } public static double GetFromPartsDouble(int sign, int exp, ulong man) { var bits = default(DoubleBits); bits.SetFromParts(sign, exp, man); return bits.Float; } public static float GetFromPartsSingle(int sign, int exp, uint man) { var bits = default(SingleBits); bits.SetFromParts(sign, exp, man); return bits.Float; } public static double GetPowerOfTwoDouble(int exp) { var bits = default(DoubleBits); bits.SetPowerOfTwo(exp); return bits.Float; } public static float GetPowerOfTwoSingle(int exp) { var bits = default(SingleBits); bits.SetPowerOfTwo(exp); return bits.Float; } // Returns the previous exponent and sets the exponent to zero. Asserts that // the original value is finite and not a denormal. public static int NormalizeExponent(ref double x) { var bits = default(DoubleBits); bits.Float = x; int expTmp = bits.GetExp(); bits.NormalizeExponent(); x = bits.Float; return expTmp; } // Returns the previous exponent and sets the exponent to zero. Asserts that // the original value is finite and not a denormal. public static int NormalizeExponent(ref float x) { var bits = default(SingleBits); bits.Float = x; int expTmp = bits.GetExp(); bits.NormalizeExponent(); x = bits.Float; return expTmp; } public static double TruncateMantissaToSingleBit(double x) { var bits = default(DoubleBits); bits.Float = x; bits.TruncateMantissaToSingleBit(); return bits.Float; } public static float TruncateMantissaToSingleBit(float x) { var bits = default(SingleBits); bits.Float = x; bits.TruncateMantissaToSingleBit(); return bits.Float; } public static double Truncate(double x) { var bits = default(DoubleBits); bits.Float = x; bits.Truncate(); return bits.Float; } public static string ToRoundTripString(float x) { return x.ToString("R", CultureInfo.InvariantCulture); } public static string ToRoundTripString(double x) { return x.ToString("G17", CultureInfo.InvariantCulture); } } }