Number
using System.Buffers.Text;
using System.Runtime.CompilerServices;
namespace System
{
internal static class Number
{
private static class DoubleHelper
{
public unsafe static uint Exponent(double d)
{
return (*(uint*)((byte*)(&d) + 4) >> 20) & 2047;
}
public unsafe static ulong Mantissa(double d)
{
return *(uint*)(&d) | ((ulong)(*(uint*)((byte*)(&d) + 4) & 1048575) << 32);
}
public unsafe static bool Sign(double d)
{
return *(uint*)((byte*)(&d) + 4) >> 31 != 0;
}
}
internal const int DECIMAL_PRECISION = 29;
private static readonly ulong[] s_rgval64Power10 = new ulong[30] {
11529215046068469760,
14411518807585587200,
18014398509481984000,
11258999068426240000,
14073748835532800000,
17592186044416000000,
10995116277760000000,
13743895347200000000,
17179869184000000000,
10737418240000000000,
13421772800000000000,
16777216000000000000,
10485760000000000000,
13107200000000000000,
16384000000000000000,
14757395258967641293,
11805916207174113035,
9444732965739290428,
15111572745182864686,
12089258196146291749,
9671406556917033399,
15474250491067253438,
12379400392853802751,
9903520314283042201,
15845632502852867522,
12676506002282294018,
10141204801825835215,
16225927682921336344,
12980742146337069075,
10384593717069655260
};
private static readonly sbyte[] s_rgexp64Power10 = new sbyte[15] {
4,
7,
10,
14,
17,
20,
24,
27,
30,
34,
37,
40,
44,
47,
50
};
private static readonly ulong[] s_rgval64Power10By16 = new ulong[42] {
10240000000000000000,
11368683772161602974,
12621774483536188886,
14012984643248170708,
15557538194652854266,
17272337110188889248,
9588073174409622172,
10644899600020376798,
11818212630765741798,
13120851772591970216,
14567071740625403792,
16172698447808779622,
17955302187076837696,
9967194951097567532,
11065809325636130658,
12285516299433008778,
13639663065038175358,
15143067982934716296,
16812182738118149112,
9332636185032188787,
10361307573072618722,
16615349947311448416,
14965776766268445891,
13479973333575319909,
12141680576410806707,
10936253623915059637,
9850501549098619819,
17745086042373215136,
15983352577617880260,
14396524142538228461,
12967236152753103031,
11679847981112819795,
10520271803096747049,
9475818434452569218,
17070116948172427008,
15375394465392026135,
13848924157002783096,
12474001934591998882,
11235582092889474480,
10120112665365530972,
18230774251475056952,
16420821625123739930
};
private static readonly short[] s_rgexp64Power10By16 = new short[21] {
54,
107,
160,
213,
266,
319,
373,
426,
479,
532,
585,
638,
691,
745,
798,
851,
904,
957,
1010,
1064,
1117
};
public static void RoundNumber(ref NumberBuffer number, int pos)
{
Span<byte> digits = number.Digits;
int i;
for (i = 0; i < pos && digits[i] != 0; i++) {
}
if (i != pos || digits[i] < 53) {
while (i > 0 && digits[i - 1] == 48) {
i--;
}
} else {
while (i > 0 && digits[i - 1] == 57) {
i--;
}
if (i > 0)
digits[i - 1]++;
else {
number.Scale++;
digits[0] = 49;
i = 1;
}
}
if (i == 0) {
number.Scale = 0;
number.IsNegative = false;
}
digits[i] = 0;
}
internal static bool NumberBufferToDouble(ref NumberBuffer number, out double value)
{
double num = NumberToDouble(ref number);
uint num2 = DoubleHelper.Exponent(num);
ulong num3 = DoubleHelper.Mantissa(num);
switch (num2) {
case 2047:
value = 0;
return false;
case 0:
if (num3 == 0)
num = 0;
break;
}
value = num;
return true;
}
public unsafe static bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value)
{
MutableDecimal source = default(MutableDecimal);
byte* ptr = number.UnsafeDigits;
int num = number.Scale;
if (*ptr == 0) {
if (num > 0)
num = 0;
} else {
if (num > 29)
return false;
while ((num > 0 || (*ptr != 0 && num > -28)) && (source.High < 429496729 || (source.High == 429496729 && (source.Mid < 2576980377 || (source.Mid == 2576980377 && (source.Low < 2576980377 || (source.Low == 2576980377 && *ptr <= 53))))))) {
DecimalDecCalc.DecMul10(ref source);
if (*ptr != 0) {
byte* intPtr = ptr;
ptr = intPtr + 1;
DecimalDecCalc.DecAddInt32(ref source, (uint)(*intPtr - 48));
}
num--;
}
byte* intPtr2 = ptr;
ptr = intPtr2 + 1;
if (*intPtr2 >= 53) {
bool flag = true;
if (*(ptr - 1) == 53 && (int)(*(ptr - 2)) % 2 == 0) {
int num2 = 20;
while (*ptr == 48 && num2 != 0) {
ptr++;
num2--;
}
if (*ptr == 0 || num2 == 0)
flag = false;
}
if (flag) {
DecimalDecCalc.DecAddInt32(ref source, 1);
if ((source.High | source.Mid | source.Low) == 0) {
source.High = 429496729;
source.Mid = 2576980377;
source.Low = 2576980378;
num++;
}
}
}
}
if (num > 0)
return false;
if (num <= -29) {
source.High = 0;
source.Low = 0;
source.Mid = 0;
source.Scale = 28;
} else
source.Scale = -num;
source.IsNegative = number.IsNegative;
value = Unsafe.As<MutableDecimal, decimal>(ref source);
return true;
}
public static void DecimalToNumber(decimal value, ref NumberBuffer number)
{
ref MutableDecimal reference = ref Unsafe.As<decimal, MutableDecimal>(ref value);
Span<byte> digits = number.Digits;
number.IsNegative = reference.IsNegative;
int num = 29;
while ((reference.Mid != 0) | (reference.High != 0)) {
uint num2 = DecimalDecCalc.DecDivMod1E9(ref reference);
for (int i = 0; i < 9; i++) {
digits[--num] = (byte)(num2 % 10 + 48);
num2 /= 10;
}
}
for (uint num3 = reference.Low; num3 != 0; num3 /= 10) {
digits[--num] = (byte)(num3 % 10 + 48);
}
int num4 = 29 - num;
number.Scale = num4 - reference.Scale;
Span<byte> digits2 = number.Digits;
int num5 = 0;
while (--num4 >= 0) {
digits2[num5++] = digits[num++];
}
digits2[num5] = 0;
}
private static uint DigitsToInt(ReadOnlySpan<byte> digits, int count)
{
uint value;
int bytesConsumed;
bool flag = Utf8Parser.TryParse(digits.Slice(0, count), out value, out bytesConsumed, 'D');
return value;
}
private static ulong Mul32x32To64(uint a, uint b)
{
return (ulong)((long)a * (long)b);
}
private static ulong Mul64Lossy(ulong a, ulong b, ref int pexp)
{
ulong num = Mul32x32To64((uint)(a >> 32), (uint)(b >> 32)) + (Mul32x32To64((uint)(a >> 32), (uint)b) >> 32) + (Mul32x32To64((uint)a, (uint)(b >> 32)) >> 32);
if (((long)num & -9223372036854775808) == 0) {
num <<= 1;
pexp--;
}
return num;
}
private static int abs(int value)
{
if (value < 0)
return -value;
return value;
}
private unsafe static double NumberToDouble(ref NumberBuffer number)
{
ReadOnlySpan<byte> digits = number.Digits;
int i = 0;
int numDigits = number.NumDigits;
int num = numDigits;
for (; digits[i] == 48; i++) {
num--;
}
if (num == 0)
return 0;
int num2 = Math.Min(num, 9);
num -= num2;
ulong num3 = DigitsToInt(digits, num2);
if (num > 0) {
num2 = Math.Min(num, 9);
num -= num2;
uint b = (uint)(s_rgval64Power10[num2 - 1] >> 64 - s_rgexp64Power10[num2 - 1]);
num3 = Mul32x32To64((uint)num3, b) + DigitsToInt(digits.Slice(9), num2);
}
int num4 = number.Scale - (numDigits - num);
int num5 = abs(num4);
if (num5 >= 352) {
ulong num6 = (ulong)((num4 > 0) ? 9218868437227405312 : 0);
if (number.IsNegative)
num6 = (ulong)((long)num6 | -9223372036854775808);
return *(double*)(&num6);
}
int pexp = 64;
if (((long)num3 & -4294967296) == 0) {
num3 <<= 32;
pexp -= 32;
}
if (((long)num3 & -281474976710656) == 0) {
num3 <<= 16;
pexp -= 16;
}
if (((long)num3 & -72057594037927936) == 0) {
num3 <<= 8;
pexp -= 8;
}
if (((long)num3 & -1152921504606846976) == 0) {
num3 <<= 4;
pexp -= 4;
}
if (((long)num3 & -4611686018427387904) == 0) {
num3 <<= 2;
pexp -= 2;
}
if (((long)num3 & -9223372036854775808) == 0) {
num3 <<= 1;
pexp--;
}
int num7 = num5 & 15;
if (num7 != 0) {
int num8 = s_rgexp64Power10[num7 - 1];
pexp += ((num4 < 0) ? (-num8 + 1) : num8);
ulong b2 = s_rgval64Power10[num7 + ((num4 < 0) ? 15 : 0) - 1];
num3 = Mul64Lossy(num3, b2, ref pexp);
}
num7 = num5 >> 4;
if (num7 != 0) {
int num9 = s_rgexp64Power10By16[num7 - 1];
pexp += ((num4 < 0) ? (-num9 + 1) : num9);
ulong b3 = s_rgval64Power10By16[num7 + ((num4 < 0) ? 21 : 0) - 1];
num3 = Mul64Lossy(num3, b3, ref pexp);
}
if (((int)num3 & 1024) != 0) {
ulong num10 = (ulong)((long)(num3 + 1023) + (long)(((int)num3 >> 11) & 1));
if (num10 < num3) {
num10 = (ulong)((long)(num10 >> 1) | -9223372036854775808);
pexp++;
}
num3 = num10;
}
pexp += 1022;
num3 = (ulong)((pexp <= 0) ? ((long)((pexp == -52 && num3 >= 9223372036854775896) ? 1 : ((pexp > -52) ? (num3 >> -pexp + 11 + 1) : 0))) : ((pexp < 2047) ? (((long)pexp << 52) + (long)((num3 >> 11) & 4503599627370495)) : 9218868437227405312));
if (number.IsNegative)
num3 = (ulong)((long)num3 | -9223372036854775808);
return *(double*)(&num3);
}
}
}