Exponential Bucket Histogram - part 6 (#3494)

This commit is contained in:
Reiley Yang 2022-07-26 16:23:31 -07:00 committed by GitHub
parent 6f2b1a0b34
commit 44ac86cb74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 269 additions and 60 deletions

View File

@ -14,7 +14,6 @@
// limitations under the License.
// </copyright>
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
@ -136,15 +135,4 @@ internal static class MathHelper
return !double.IsInfinity(value) && !double.IsNaN(value);
#endif
}
public static string DoubleToString(double value)
{
var repr = Convert.ToString(BitConverter.DoubleToInt64Bits(value), 2);
return new string('0', 64 - repr.Length) + repr;
}
public static double DoubleFromString(string value)
{
return BitConverter.Int64BitsToDouble(Convert.ToInt64(value, 2));
}
}

View File

@ -37,7 +37,49 @@ internal class ExponentialBucketHistogram
public ExponentialBucketHistogram(int scale, int maxBuckets = 160)
{
Guard.ThrowIfOutOfRange(scale, min: -20, max: 20); // TODO: calculate the actual range
/*
The following table is calculated based on [ MapToIndex(double.Epsilon), MapToIndex(double.MaxValue) ]:
| Scale | Index Range |
| ----- | ------------------------- |
| < -11 | [-1, 0] |
| -11 | [-1, 0] |
| -10 | [-2, 0] |
| -9 | [-3, 1] |
| -8 | [-5, 3] |
| -7 | [-9, 7] |
| -6 | [-17, 15] |
| -5 | [-34, 31] |
| -4 | [-68, 63] |
| -3 | [-135, 127] |
| -2 | [-269, 255] |
| -1 | [-538, 511] |
| 0 | [-1075, 1023] |
| 1 | [-2149, 2047] |
| 2 | [-4297, 4095] |
| 3 | [-8593, 8191] |
| 4 | [-17185, 16383] |
| 5 | [-34369, 32767] |
| 6 | [-68737, 65535] |
| 7 | [-137473, 131071] |
| 8 | [-274945, 262143] |
| 9 | [-549889, 524287] |
| 10 | [-1099777, 1048575] |
| 11 | [-2199553, 2097151] |
| 12 | [-4399105, 4194303] |
| 13 | [-8798209, 8388607] |
| 14 | [-17596417, 16777215] |
| 15 | [-35192833, 33554431] |
| 16 | [-70385665, 67108863] |
| 17 | [-140771329, 134217727] |
| 18 | [-281542657, 268435455] |
| 19 | [-563085313, 536870911] |
| 20 | [-1126170625, 1073741823] |
| 21 | [underflow, 2147483647] |
| > 21 | [underflow, overflow] |
*/
Guard.ThrowIfOutOfRange(scale, min: -11, max: 20);
Guard.ThrowIfOutOfRange(maxBuckets, min: 1);
this.Scale = scale;
@ -88,17 +130,26 @@ internal class ExponentialBucketHistogram
Debug.Assert(value != 0, "IEEE-754 zero values should be handled by ZeroCount.");
Debug.Assert(!double.IsNegative(value), "IEEE-754 negative values should be normalized before calling this method.");
var bits = BitConverter.DoubleToInt64Bits(value);
var fraction = bits & 0xFFFFFFFFFFFFFL /* fraction mask */;
if (this.Scale > 0)
{
// TODO: do we really need this given the lookup table is needed for scale>0 anyways?
if (fraction == 0)
{
var exp = (int)((bits & 0x7FF0000000000000L /* exponent mask */) >> 52 /* fraction width */);
return ((exp - 1023 /* exponent bias */) << this.Scale) - 1;
}
// TODO: due to precision issue, the values that are close to the bucket
// boundaries should be closely examined to avoid off-by-one.
return (int)Math.Ceiling(Math.Log(value) * this.scalingFactor) - 1;
}
else
{
var bits = BitConverter.DoubleToInt64Bits(value);
var exp = (int)((bits & 0x7FF0000000000000L /* exponent mask */) >> 52 /* fraction width */);
var fraction = bits & 0xFFFFFFFFFFFFFL /* fraction mask */;
if (exp == 0)
{

View File

@ -17,6 +17,7 @@
#if NET6_0_OR_GREATER
using System;
using OpenTelemetry.Tests;
using Xunit;
namespace OpenTelemetry.Metrics.Tests;
@ -40,47 +41,123 @@ public class ExponentialBucketHistogramTest
// bucket[3]: (8, 16]
// ...
var histogram_scale0 = new ExponentialBucketHistogram(0);
var histogram = new ExponentialBucketHistogram(0);
Assert.Equal(-1075, histogram_scale0.MapToIndex(double.Epsilon));
Assert.Equal(-1075, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000001"))); // ~4.9406564584124654E-324 (minimum subnormal positive, double.Epsilon)
Assert.Equal(-1074, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000010"))); // double.Epsilon * 2
Assert.Equal(-1073, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000011"))); // double.Epsilon * 3
Assert.Equal(-1073, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000100"))); // double.Epsilon * 4
Assert.Equal(-1072, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000101"))); // double.Epsilon * 5
Assert.Equal(-1072, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000110"))); // double.Epsilon * 6
Assert.Equal(-1072, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000111"))); // double.Epsilon * 7
Assert.Equal(-1072, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000001000"))); // double.Epsilon * 8
Assert.Equal(-1024, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308
Assert.Equal(-1023, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000001"))); // ~1.112536929253601E-308
Assert.Equal(-1023, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1111111111111111111111111111111111111111111111111111"))); // ~2.2250738585072009E-308 (maximum subnormal positive)
Assert.Equal(-1023, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive)
Assert.Equal(-1022, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000001"))); // ~2.225073858507202E-308
Assert.Equal(-3, histogram.MapToIndex(IEEE754Double.FromString("0 01111111101 0000000000000000000000000000000000000000000000000000"))); // 0.25
Assert.Equal(-2, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000000"))); // 0.5
Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000001"))); // ~0.5000000000000001
Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000000"))); // 1
Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000001"))); // ~1.0000000000000002
Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000000 0000000000000000000000000000000000000000000000000000"))); // 2
Assert.Equal(1, histogram.MapToIndex(IEEE754Double.FromString("0 10000000001 0000000000000000000000000000000000000000000000000000"))); // 4
Assert.Equal(2, histogram.MapToIndex(IEEE754Double.FromString("0 10000000010 0000000000000000000000000000000000000000000000000000"))); // 8
Assert.Equal(3, histogram.MapToIndex(IEEE754Double.FromString("0 10000000011 0000000000000000000000000000000000000000000000000000"))); // 16
Assert.Equal(4, histogram.MapToIndex(IEEE754Double.FromString("0 10000000100 0000000000000000000000000000000000000000000000000000"))); // 32
Assert.Equal(1022, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000000"))); // ~8.98846567431158E+307
Assert.Equal(1023, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000001"))); // ~8.988465674311582E+307
Assert.Equal(1023, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111110"))); // ~1.7976931348623155E+308
Assert.Equal(1023, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111111"))); // ~1.7976931348623157E+308 (maximum normal positive, double.MaxValue)
Assert.Equal(-1074, histogram_scale0.MapToIndex(double.Epsilon * 2));
// An exponential bucket histogram with scale = -1.
// The base is 2 ^ (2 ^ 1) = 4.
// The buckets are:
//
// ...
// bucket[-3]: (1/64, 1/16]
// bucket[-2]: (1/16, 1/4]
// bucket[-1]: (1/4, 1]
// bucket[0]: (1, 4]
// bucket[1]: (4, 16]
// bucket[2]: (16, 64]
// bucket[3]: (64, 256]
// ...
Assert.Equal(-1073, histogram_scale0.MapToIndex(double.Epsilon * 3));
Assert.Equal(-1073, histogram_scale0.MapToIndex(double.Epsilon * 4));
histogram = new ExponentialBucketHistogram(-1);
Assert.Equal(-1072, histogram_scale0.MapToIndex(double.Epsilon * 5));
Assert.Equal(-1072, histogram_scale0.MapToIndex(double.Epsilon * 6));
Assert.Equal(-1072, histogram_scale0.MapToIndex(double.Epsilon * 7));
Assert.Equal(-1072, histogram_scale0.MapToIndex(double.Epsilon * 8));
Assert.Equal(-538, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000001"))); // ~4.9406564584124654E-324 (minimum subnormal positive, double.Epsilon)
Assert.Equal(-537, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000010"))); // double.Epsilon * 2
Assert.Equal(-537, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000011"))); // double.Epsilon * 3
Assert.Equal(-537, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000100"))); // double.Epsilon * 4
Assert.Equal(-536, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000101"))); // double.Epsilon * 5
Assert.Equal(-536, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000110"))); // double.Epsilon * 6
Assert.Equal(-536, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000111"))); // double.Epsilon * 7
Assert.Equal(-536, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000001000"))); // double.Epsilon * 8
Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308
Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000001"))); // ~1.112536929253601E-308
Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1111111111111111111111111111111111111111111111111111"))); // ~2.2250738585072009E-308 (maximum subnormal positive)
Assert.Equal(-512, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive)
Assert.Equal(-511, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000001"))); // ~2.225073858507202E-308
Assert.Equal(-2, histogram.MapToIndex(IEEE754Double.FromString("0 01111111101 0000000000000000000000000000000000000000000000000000"))); // 0.25
Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000000"))); // 0.5
Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000001"))); // ~0.5000000000000001
Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000000"))); // 1
Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000001"))); // ~1.0000000000000002
Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000000 0000000000000000000000000000000000000000000000000000"))); // 2
Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000001 0000000000000000000000000000000000000000000000000000"))); // 4
Assert.Equal(1, histogram.MapToIndex(IEEE754Double.FromString("0 10000000010 0000000000000000000000000000000000000000000000000000"))); // 8
Assert.Equal(1, histogram.MapToIndex(IEEE754Double.FromString("0 10000000011 0000000000000000000000000000000000000000000000000000"))); // 16
Assert.Equal(2, histogram.MapToIndex(IEEE754Double.FromString("0 10000000100 0000000000000000000000000000000000000000000000000000"))); // 32
Assert.Equal(511, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000000"))); // ~8.98846567431158E+307
Assert.Equal(511, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000001"))); // ~8.988465674311582E+307
Assert.Equal(511, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111110"))); // ~1.7976931348623155E+308
Assert.Equal(511, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111111"))); // ~1.7976931348623157E+308 (maximum normal positive, double.MaxValue)
Assert.Equal(-1023, histogram_scale0.MapToIndex(2.2250738585072009E-308));
Assert.Equal(-1023, histogram_scale0.MapToIndex(2.2250738585072014E-308));
// An exponential bucket histogram with scale = -2.
// The base is 2 ^ (2 ^ 2) = 16.
// The buckets are:
//
// ...
// bucket[-3]: (1/4096, 1/256]
// bucket[-2]: (1/256, 1/16]
// bucket[-1]: (1/16, 1]
// bucket[0]: (1, 16]
// bucket[1]: (16, 256]
// bucket[2]: (256, 4096]
// bucket[3]: (4096, 65536]
// ...
Assert.Equal(-3, histogram_scale0.MapToIndex(0.25));
histogram = new ExponentialBucketHistogram(-2);
Assert.Equal(-2, histogram_scale0.MapToIndex(0.375));
Assert.Equal(-2, histogram_scale0.MapToIndex(0.5));
Assert.Equal(-1, histogram_scale0.MapToIndex(0.75));
Assert.Equal(-1, histogram_scale0.MapToIndex(1));
Assert.Equal(0, histogram_scale0.MapToIndex(1.5));
Assert.Equal(0, histogram_scale0.MapToIndex(2));
Assert.Equal(1, histogram_scale0.MapToIndex(3));
Assert.Equal(1, histogram_scale0.MapToIndex(4));
Assert.Equal(2, histogram_scale0.MapToIndex(5));
Assert.Equal(2, histogram_scale0.MapToIndex(6));
Assert.Equal(2, histogram_scale0.MapToIndex(7));
Assert.Equal(2, histogram_scale0.MapToIndex(8));
Assert.Equal(3, histogram_scale0.MapToIndex(9));
Assert.Equal(3, histogram_scale0.MapToIndex(16));
Assert.Equal(4, histogram_scale0.MapToIndex(17));
Assert.Equal(4, histogram_scale0.MapToIndex(32));
Assert.Equal(-269, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000001"))); // ~4.9406564584124654E-324 (minimum subnormal positive, double.Epsilon)
Assert.Equal(-269, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000010"))); // double.Epsilon * 2
Assert.Equal(-269, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000011"))); // double.Epsilon * 3
Assert.Equal(-269, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000100"))); // double.Epsilon * 4
Assert.Equal(-268, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000101"))); // double.Epsilon * 5
Assert.Equal(-268, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000110"))); // double.Epsilon * 6
Assert.Equal(-268, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000000111"))); // double.Epsilon * 7
Assert.Equal(-268, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 0000000000000000000000000000000000000000000000001000"))); // double.Epsilon * 8
Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000000"))); // ~1.1125369292536007E-308
Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1000000000000000000000000000000000000000000000000001"))); // ~1.112536929253601E-308
Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000000 1111111111111111111111111111111111111111111111111111"))); // ~2.2250738585072009E-308 (maximum subnormal positive)
Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000000"))); // ~2.2250738585072014E-308 (minimum normal positive)
Assert.Equal(-256, histogram.MapToIndex(IEEE754Double.FromString("0 00000000001 0000000000000000000000000000000000000000000000000001"))); // ~2.225073858507202E-308
Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111101 0000000000000000000000000000000000000000000000000000"))); // 0.25
Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000000"))); // 0.5
Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111110 0000000000000000000000000000000000000000000000000001"))); // ~0.5000000000000001
Assert.Equal(-1, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000000"))); // 1
Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 01111111111 0000000000000000000000000000000000000000000000000001"))); // ~1.0000000000000002
Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000000 0000000000000000000000000000000000000000000000000000"))); // 2
Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000001 0000000000000000000000000000000000000000000000000000"))); // 4
Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000010 0000000000000000000000000000000000000000000000000000"))); // 8
Assert.Equal(0, histogram.MapToIndex(IEEE754Double.FromString("0 10000000011 0000000000000000000000000000000000000000000000000000"))); // 16
Assert.Equal(1, histogram.MapToIndex(IEEE754Double.FromString("0 10000000100 0000000000000000000000000000000000000000000000000000"))); // 32
Assert.Equal(255, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000000"))); // ~8.98846567431158E+307
Assert.Equal(255, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 0000000000000000000000000000000000000000000000000001"))); // ~8.988465674311582E+307
Assert.Equal(255, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111110"))); // ~1.7976931348623155E+308
Assert.Equal(255, histogram.MapToIndex(IEEE754Double.FromString("0 11111111110 1111111111111111111111111111111111111111111111111111"))); // ~1.7976931348623157E+308 (maximum normal positive, double.MaxValue)
// An exponential bucket histogram with scale = 1.
// The base is 2 ^ (2 ^ -1) = sqrt(2) = 1.41421356237.
@ -96,17 +173,7 @@ public class ExponentialBucketHistogramTest
// bucket[3]: (2.82842712474, 4]
// ...
var histogram_scale1 = new ExponentialBucketHistogram(1);
Assert.Equal(-3, histogram_scale1.MapToIndex(0.5));
Assert.Equal(-2, histogram_scale1.MapToIndex(0.6));
Assert.Equal(-1, histogram_scale1.MapToIndex(1));
Assert.Equal(1, histogram_scale1.MapToIndex(2));
Assert.Equal(3, histogram_scale1.MapToIndex(4));
histogram = new ExponentialBucketHistogram(1);
}
}

View File

@ -0,0 +1,103 @@
// <copyright file="IEEE754Double.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
using System;
using System.Runtime.InteropServices;
namespace OpenTelemetry.Tests;
[StructLayout(LayoutKind.Explicit)]
public struct IEEE754Double
{
[FieldOffset(0)]
public double DoubleValue = 0;
[FieldOffset(0)]
public long LongValue = 0;
[FieldOffset(0)]
public ulong ULongValue = 0;
public IEEE754Double(double value)
{
this.DoubleValue = value;
}
public static implicit operator double(IEEE754Double value)
{
return value.DoubleValue;
}
public static IEEE754Double operator ++(IEEE754Double value)
{
value.ULongValue++;
return value;
}
public static IEEE754Double operator --(IEEE754Double value)
{
value.ULongValue--;
return value;
}
public static IEEE754Double FromDouble(double value)
{
return new IEEE754Double(value);
}
public static IEEE754Double FromLong(long value)
{
return new IEEE754Double { LongValue = value };
}
public static IEEE754Double FromULong(ulong value)
{
return new IEEE754Double { ULongValue = value };
}
public static IEEE754Double FromString(string value)
{
return IEEE754Double.FromLong(Convert.ToInt64(value.Replace(" ", string.Empty), 2));
}
public override string ToString()
{
Span<char> chars = stackalloc char[66];
var bits = this.ULongValue;
var index = chars.Length - 1;
for (int i = 0; i < 52; i++)
{
chars[index--] = (char)(bits & 0x01 | 0x30);
bits >>= 1;
}
chars[index--] = ' ';
for (int i = 0; i < 11; i++)
{
chars[index--] = (char)(bits & 0x01 | 0x30);
bits >>= 1;
}
chars[index--] = ' ';
chars[index--] = (char)(bits & 0x01 | 0x30);
return $"{chars.ToString()} ({this.DoubleValue})";
}
}