Summary

Class:System.Number
Assembly:System.Memory
File(s):C:\GitHub\corefx\src\System.Memory\src\System\Number\Number.cs
C:\GitHub\corefx\src\System.Memory\src\System\Number\Number.FormatAndParse.cs
Covered lines:392
Uncovered lines:7
Coverable lines:399
Total lines:593
Line coverage:98.2%
Branch coverage:98%

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
RoundNumber(...)1164100100
NumberBufferToDouble(...)44100100
NumberBufferToDecimal(...)32819295.3196.30
DecimalToNumber(...)516100100
DigitsToInt(...)10100100
Mul32x32To64(...)10100100
Mul64Lossy(...)22100100
abs(...)22100100
NumberToDouble(...)313355443296.4698.04
.cctor()10100100
Exponent(...)10100100
Mantissa(...)10100100

File(s)

C:\GitHub\corefx\src\System.Memory\src\System\Number\Number.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3// See the LICENSE file in the project root for more information.
 4
 5//
 6// This code is copied almost verbatim from the same-named file in CoreRT with mechanical changes to Span-ify it.
 7//
 8
 9namespace System
 10{
 11    internal static partial class Number
 12    {
 13        internal const int DECIMAL_PRECISION = 29;
 14
 15        //
 16        // This method is copied directly from CoreRT (which is in turn a C#-ized version of the CoreCLR C++ code.)
 17        //
 18        public static void RoundNumber(ref NumberBuffer number, int pos)
 119        {
 120            number.CheckConsistency();
 21
 122            Span<byte> digits = number.Digits;
 23
 124            int i = 0;
 125            while (i < pos && digits[i] != 0)
 126                i++;
 27
 128            if (i == pos && digits[i] >= (byte)'5')
 129            {
 130                while (i > 0 && digits[i - 1] == (byte)'9')
 131                    i--;
 32
 133                if (i > 0)
 134                {
 135                    digits[i - 1]++;
 136                }
 37                else
 138                {
 139                    number.Scale++;
 140                    digits[0] = (byte)'1';
 141                    i = 1;
 142                }
 143            }
 44            else
 145            {
 146                while (i > 0 && digits[i - 1] == (byte)'0')
 147                    i--;
 148            }
 149            if (i == 0)
 150            {
 151                number.Scale = 0;
 152                number.IsNegative = false;
 153            }
 154            digits[i] = 0;
 55
 156            number.CheckConsistency();
 157        }
 58    }
 59}

C:\GitHub\corefx\src\System.Memory\src\System\Number\Number.FormatAndParse.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3// See the LICENSE file in the project root for more information.
 4
 5using System.Diagnostics;
 6using System.Buffers.Text;
 7
 8#if !netstandard
 9using Internal.Runtime.CompilerServices;
 10#else
 11using System.Runtime.CompilerServices;
 12#endif
 13
 14//
 15// This code is copied almost verbatim from the same-named file in CoreRT with mechanical changes to Span-ify it.
 16//
 17
 18namespace System
 19{
 20    internal static partial class Number
 21    {
 22        //
 23        // Convert a Number to a double.
 24        //
 25        internal static bool NumberBufferToDouble(ref NumberBuffer number, out double value)
 126        {
 127            double d = NumberToDouble(ref number);
 28
 129            uint e = DoubleHelper.Exponent(d);
 130            ulong m = DoubleHelper.Mantissa(d);
 131            if (e == 0x7FF)
 132            {
 133                value = default;
 134                return false;
 35            }
 36
 137            if (e == 0 && m == 0)
 138            {
 139                d = 0;
 140            }
 41
 142            value = d;
 43
 144            return true;
 145        }
 46
 47        public static unsafe bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value)
 148        {
 149            MutableDecimal d = new MutableDecimal();
 50
 151            byte* p = number.UnsafeDigits;
 152            int e = number.Scale;
 153            if (*p == 0)
 154            {
 55                // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to e
 56                // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.
 157                if (e > 0)
 058                {
 059                    e = 0;
 060                }
 161            }
 62            else
 163            {
 164                if (e > DECIMAL_PRECISION)
 165                    return false;
 66
 167                while (((e > 0) || ((*p != 0) && (e > -28))) &&
 168                       ((d.High < 0x19999999) || ((d.High == 0x19999999) &&
 169                                                  ((d.Mid < 0x99999999) || ((d.Mid == 0x99999999) &&
 170                                                                            ((d.Low < 0x99999999) || ((d.Low == 0x999999
 171                                                                                                      (*p <= '5'))))))))
 172                {
 173                    DecimalDecCalc.DecMul10(ref d);
 174                    if (*p != 0)
 175                        DecimalDecCalc.DecAddInt32(ref d, (uint)(*p++ - '0'));
 176                    e--;
 177                }
 78
 179                if (*p++ >= '5')
 180                {
 181                    bool round = true;
 182                    if ((*(p - 1) == '5') && ((*(p - 2) % 2) == 0))
 183                    {
 84                        // Check if previous digit is even, only if the when we are unsure whether hows to do
 85                        // Banker's rounding. For digits > 5 we will be rounding up anyway.
 186                        int count = 20; // Look at the next 20 digits to check to round
 187                        while ((*p == '0') && (count != 0))
 188                        {
 189                            p++;
 190                            count--;
 191                        }
 192                        if ((*p == '\0') || (count == 0))
 193                            round = false;// Do nothing
 194                    }
 95
 196                    if (round)
 197                    {
 198                        DecimalDecCalc.DecAddInt32(ref d, 1);
 199                        if ((d.High | d.Mid | d.Low) == 0)
 1100                        {
 101                            // If we got here, the magnitude portion overflowed and wrapped back to 0 as the magnitude w
 102                            //
 103                            //     79,228,162,514,264,337,593,543,950,335e+X
 104                            //
 105                            // Manually force it to the correct result:
 106                            //
 107                            //      7,922,816,251,426,433,759,354,395,034e+(X+1)
 108                            //
 109                            // This code path can be reached by trying to parse the following as a Decimal:
 110                            //
 111                            //      0.792281625142643375935439503355e28
 112                            //
 113
 1114                            d.High = 0x19999999;
 1115                            d.Mid = 0x99999999;
 1116                            d.Low = 0x9999999A;
 1117                            e++;
 1118                        }
 1119                    }
 1120                }
 1121            }
 122
 1123            if (e > 0)
 1124                return false; // Rounding may have caused its own overflow. For example, parsing "0.79228162514264337593
 125
 1126            if (e <= -DECIMAL_PRECISION)
 1127            {
 128                // Parsing a large scale zero can give you more precision than fits in the decimal.
 129                // This should only happen for actual zeros or very small numbers that round to zero.
 1130                d.High = 0;
 1131                d.Low = 0;
 1132                d.Mid = 0;
 1133                d.Scale = DECIMAL_PRECISION - 1;
 1134            }
 135            else
 1136            {
 1137                d.Scale = -e;
 1138            }
 1139            d.IsNegative = number.IsNegative;
 140
 1141            value = Unsafe.As<MutableDecimal, decimal>(ref d);
 1142            return true;
 1143        }
 144
 145        public static void DecimalToNumber(decimal value, ref NumberBuffer number)
 1146        {
 1147            ref MutableDecimal d = ref Unsafe.As<decimal, MutableDecimal>(ref value);
 148
 1149            Span<byte> buffer = number.Digits;
 1150            number.IsNegative = d.IsNegative;
 151
 1152            int index = DECIMAL_PRECISION;
 153
 154            // Starting from the least significant bits, carve off nine decimal digits at a time and string-ize them (us
 155            // buffer as a scratch buffer.)
 1156            while (d.Mid != 0 | d.High != 0)
 1157            {
 1158                uint modulo1E9 = DecimalDecCalc.DecDivMod1E9(ref d);
 3159                for (int digitCount = 0; digitCount < 9; digitCount++)
 1160                {
 1161                    buffer[--index] = (byte)(modulo1E9 % 10 + '0');
 1162                    modulo1E9 /= 10;
 1163                }
 1164            }
 165
 166            // We've finally whittled the decimal down to uint.MaxValue or less. Write the remaining digits but make sur
 1167            uint remainder = d.Low;
 1168            while (remainder != 0)
 1169            {
 1170                buffer[--index] = (byte)(remainder % 10 + '0');
 1171                remainder /= 10;
 1172            }
 173
 1174            int i = DECIMAL_PRECISION - index;
 1175            number.Scale = i - d.Scale;
 176
 177            // Move the result from the end of the buffer to the beginning where we need it.
 1178            Span<byte> dst = number.Digits;
 1179            int dstIndex = 0;
 1180            while (--i >= 0)
 1181            {
 1182                dst[dstIndex++] = buffer[index++];
 1183            }
 1184            dst[dstIndex] = 0;
 185
 1186            number.CheckConsistency();
 1187        }
 188
 189        //
 190        // get 32-bit integer from at most 9 digits
 191        //
 192        private static uint DigitsToInt(ReadOnlySpan<byte> digits, int count)
 1193        {
 1194            bool success = Utf8Parser.TryParse(digits.Slice(0, count), out uint value, out int bytesConsumed, 'D');
 1195            Debug.Assert(success); // This is only called on the contents of a trusted Number structure.
 1196            return value;
 1197        }
 198
 199        //
 200        // helper to multiply two 32-bit uints
 201        //
 202        private static ulong Mul32x32To64(uint a, uint b)
 1203        {
 1204            return a * (ulong)b;
 1205        }
 206
 207        //
 208        // multiply two numbers in the internal integer representation
 209        //
 210        private static ulong Mul64Lossy(ulong a, ulong b, ref int pexp)
 1211        {
 212            // it's ok to lose some precision here - Mul64 will be called
 213            // at most twice during the conversion, so the error won't propagate
 214            // to any of the 53 significant bits of the result
 1215            ulong val = Mul32x32To64((uint)(a >> 32), (uint)(b >> 32)) +
 1216                (Mul32x32To64((uint)(a >> 32), (uint)(b)) >> 32) +
 1217                (Mul32x32To64((uint)(a), (uint)(b >> 32)) >> 32);
 218
 219            // normalize
 1220            if ((val & 0x8000000000000000) == 0)
 1221            {
 1222                val <<= 1;
 1223                pexp -= 1;
 1224            }
 225
 1226            return val;
 1227        }
 228
 229        //
 230        // precomputed tables with powers of 10. These allows us to do at most
 231        // two Mul64 during the conversion. This is important not only
 232        // for speed, but also for precision because of Mul64 computes with 1 bit error.
 233        //
 234
 1235        private static readonly ulong[] s_rgval64Power10 =
 1236        {
 1237            // powers of 10
 1238            /*1*/ 0xa000000000000000,
 1239            /*2*/ 0xc800000000000000,
 1240            /*3*/ 0xfa00000000000000,
 1241            /*4*/ 0x9c40000000000000,
 1242            /*5*/ 0xc350000000000000,
 1243            /*6*/ 0xf424000000000000,
 1244            /*7*/ 0x9896800000000000,
 1245            /*8*/ 0xbebc200000000000,
 1246            /*9*/ 0xee6b280000000000,
 1247            /*10*/ 0x9502f90000000000,
 1248            /*11*/ 0xba43b74000000000,
 1249            /*12*/ 0xe8d4a51000000000,
 1250            /*13*/ 0x9184e72a00000000,
 1251            /*14*/ 0xb5e620f480000000,
 1252            /*15*/ 0xe35fa931a0000000,
 1253
 1254            // powers of 0.1
 1255            /*1*/ 0xcccccccccccccccd,
 1256            /*2*/ 0xa3d70a3d70a3d70b,
 1257            /*3*/ 0x83126e978d4fdf3c,
 1258            /*4*/ 0xd1b71758e219652e,
 1259            /*5*/ 0xa7c5ac471b478425,
 1260            /*6*/ 0x8637bd05af6c69b7,
 1261            /*7*/ 0xd6bf94d5e57a42be,
 1262            /*8*/ 0xabcc77118461ceff,
 1263            /*9*/ 0x89705f4136b4a599,
 1264            /*10*/ 0xdbe6fecebdedd5c2,
 1265            /*11*/ 0xafebff0bcb24ab02,
 1266            /*12*/ 0x8cbccc096f5088cf,
 1267            /*13*/ 0xe12e13424bb40e18,
 1268            /*14*/ 0xb424dc35095cd813,
 1269            /*15*/ 0x901d7cf73ab0acdc,
 1270        };
 271
 1272        private static readonly sbyte[] s_rgexp64Power10 =
 1273        {
 1274            // exponents for both powers of 10 and 0.1
 1275            /*1*/ 4,
 1276            /*2*/ 7,
 1277            /*3*/ 10,
 1278            /*4*/ 14,
 1279            /*5*/ 17,
 1280            /*6*/ 20,
 1281            /*7*/ 24,
 1282            /*8*/ 27,
 1283            /*9*/ 30,
 1284            /*10*/ 34,
 1285            /*11*/ 37,
 1286            /*12*/ 40,
 1287            /*13*/ 44,
 1288            /*14*/ 47,
 1289            /*15*/ 50,
 1290        };
 291
 1292        private static readonly ulong[] s_rgval64Power10By16 =
 1293        {
 1294            // powers of 10^16
 1295            /*1*/ 0x8e1bc9bf04000000,
 1296            /*2*/ 0x9dc5ada82b70b59e,
 1297            /*3*/ 0xaf298d050e4395d6,
 1298            /*4*/ 0xc2781f49ffcfa6d4,
 1299            /*5*/ 0xd7e77a8f87daf7fa,
 1300            /*6*/ 0xefb3ab16c59b14a0,
 1301            /*7*/ 0x850fadc09923329c,
 1302            /*8*/ 0x93ba47c980e98cde,
 1303            /*9*/ 0xa402b9c5a8d3a6e6,
 1304            /*10*/ 0xb616a12b7fe617a8,
 1305            /*11*/ 0xca28a291859bbf90,
 1306            /*12*/ 0xe070f78d39275566,
 1307            /*13*/ 0xf92e0c3537826140,
 1308            /*14*/ 0x8a5296ffe33cc92c,
 1309            /*15*/ 0x9991a6f3d6bf1762,
 1310            /*16*/ 0xaa7eebfb9df9de8a,
 1311            /*17*/ 0xbd49d14aa79dbc7e,
 1312            /*18*/ 0xd226fc195c6a2f88,
 1313            /*19*/ 0xe950df20247c83f8,
 1314            /*20*/ 0x81842f29f2cce373,
 1315            /*21*/ 0x8fcac257558ee4e2,
 1316
 1317            // powers of 0.1^16
 1318            /*1*/ 0xe69594bec44de160,
 1319            /*2*/ 0xcfb11ead453994c3,
 1320            /*3*/ 0xbb127c53b17ec165,
 1321            /*4*/ 0xa87fea27a539e9b3,
 1322            /*5*/ 0x97c560ba6b0919b5,
 1323            /*6*/ 0x88b402f7fd7553ab,
 1324            /*7*/ 0xf64335bcf065d3a0,
 1325            /*8*/ 0xddd0467c64bce4c4,
 1326            /*9*/ 0xc7caba6e7c5382ed,
 1327            /*10*/ 0xb3f4e093db73a0b7,
 1328            /*11*/ 0xa21727db38cb0053,
 1329            /*12*/ 0x91ff83775423cc29,
 1330            /*13*/ 0x8380dea93da4bc82,
 1331            /*14*/ 0xece53cec4a314f00,
 1332            /*15*/ 0xd5605fcdcf32e217,
 1333            /*16*/ 0xc0314325637a1978,
 1334            /*17*/ 0xad1c8eab5ee43ba2,
 1335            /*18*/ 0x9becce62836ac5b0,
 1336            /*19*/ 0x8c71dcd9ba0b495c,
 1337            /*20*/ 0xfd00b89747823938,
 1338            /*21*/ 0xe3e27a444d8d991a,
 1339        };
 340
 1341        private static readonly short[] s_rgexp64Power10By16 =
 1342        {
 1343            // exponents for both powers of 10^16 and 0.1^16
 1344            /*1*/ 54,
 1345            /*2*/ 107,
 1346            /*3*/ 160,
 1347            /*4*/ 213,
 1348            /*5*/ 266,
 1349            /*6*/ 319,
 1350            /*7*/ 373,
 1351            /*8*/ 426,
 1352            /*9*/ 479,
 1353            /*10*/ 532,
 1354            /*11*/ 585,
 1355            /*12*/ 638,
 1356            /*13*/ 691,
 1357            /*14*/ 745,
 1358            /*15*/ 798,
 1359            /*16*/ 851,
 1360            /*17*/ 904,
 1361            /*18*/ 957,
 1362            /*19*/ 1010,
 1363            /*20*/ 1064,
 1364            /*21*/ 1117,
 1365        };
 366
 367        private static int abs(int value)
 1368        {
 1369            if (value < 0)
 1370                return -value;
 1371            return value;
 1372        }
 373
 374        private static unsafe double NumberToDouble(ref NumberBuffer number)
 1375        {
 376            ulong val;
 377            int exp;
 1378            ReadOnlySpan<byte> src = number.Digits;
 379            int remaining;
 380            int total;
 381            int count;
 382            int scale;
 383            int absscale;
 384            int index;
 1385            int srcIndex = 0;
 386
 1387            total = number.NumDigits;
 1388            remaining = total;
 389
 390            // skip the leading zeros
 1391            while (src[srcIndex] == '0')
 0392            {
 0393                remaining--;
 0394                srcIndex++;
 0395            }
 396
 1397            if (remaining == 0)
 1398                return 0;
 399
 1400            count = Math.Min(remaining, 9);
 1401            remaining -= count;
 1402            val = DigitsToInt(src, count);
 403
 1404            if (remaining > 0)
 1405            {
 1406                count = Math.Min(remaining, 9);
 1407                remaining -= count;
 408
 409                // get the denormalized power of 10
 1410                uint mult = (uint)(s_rgval64Power10[count - 1] >> (64 - s_rgexp64Power10[count - 1]));
 1411                val = Mul32x32To64((uint)val, mult) + DigitsToInt(src.Slice(9), count);
 1412            }
 413
 1414            scale = number.Scale - (total - remaining);
 1415            absscale = abs(scale);
 1416            if (absscale >= 22 * 16)
 1417            {
 418                // overflow / underflow
 1419                ulong result = (scale > 0) ? 0x7FF0000000000000 : 0ul;
 1420                if (number.IsNegative)
 1421                    result |= 0x8000000000000000;
 1422                return *(double*)&result;
 423            }
 424
 1425            exp = 64;
 426
 427            // normalize the mantissa
 1428            if ((val & 0xFFFFFFFF00000000) == 0)
 4429            { val <<= 32; exp -= 32; }
 1430            if ((val & 0xFFFF000000000000) == 0)
 4431            { val <<= 16; exp -= 16; }
 1432            if ((val & 0xFF00000000000000) == 0)
 4433            { val <<= 8; exp -= 8; }
 1434            if ((val & 0xF000000000000000) == 0)
 4435            { val <<= 4; exp -= 4; }
 1436            if ((val & 0xC000000000000000) == 0)
 4437            { val <<= 2; exp -= 2; }
 1438            if ((val & 0x8000000000000000) == 0)
 4439            { val <<= 1; exp -= 1; }
 440
 1441            index = absscale & 15;
 1442            if (index != 0)
 1443            {
 1444                int multexp = s_rgexp64Power10[index - 1];
 445                // the exponents are shared between the inverted and regular table
 1446                exp += (scale < 0) ? (-multexp + 1) : multexp;
 447
 1448                ulong multval = s_rgval64Power10[index + ((scale < 0) ? 15 : 0) - 1];
 1449                val = Mul64Lossy(val, multval, ref exp);
 1450            }
 451
 1452            index = absscale >> 4;
 1453            if (index != 0)
 1454            {
 1455                int multexp = s_rgexp64Power10By16[index - 1];
 456                // the exponents are shared between the inverted and regular table
 1457                exp += (scale < 0) ? (-multexp + 1) : multexp;
 458
 1459                ulong multval = s_rgval64Power10By16[index + ((scale < 0) ? 21 : 0) - 1];
 1460                val = Mul64Lossy(val, multval, ref exp);
 1461            }
 462
 463            // round & scale down
 1464            if (((int)val & (1 << 10)) != 0)
 1465            {
 466                // IEEE round to even
 1467                ulong tmp = val + ((1 << 10) - 1) + (ulong)(((int)val >> 11) & 1);
 1468                if (tmp < val)
 1469                {
 470                    // overflow
 1471                    tmp = (tmp >> 1) | 0x8000000000000000;
 1472                    exp += 1;
 1473                }
 1474                val = tmp;
 1475            }
 476
 477            // return the exponent to a biased state
 1478            exp += 0x3FE;
 479
 480            // handle overflow, underflow, "Epsilon - 1/2 Epsilon", denormalized, and the normal case
 1481            if (exp <= 0)
 1482            {
 1483                if (exp == -52 && (val >= 0x8000000000000058))
 1484                {
 485                    // round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon (instead of down to 
 1486                    val = 0x0000000000000001;
 1487                }
 1488                else if (exp <= -52)
 1489                {
 490                    // underflow
 1491                    val = 0;
 1492                }
 493                else
 1494                {
 495                    // denormalized
 1496                    val >>= (-exp + 11 + 1);
 1497                }
 1498            }
 1499            else if (exp >= 0x7FF)
 1500            {
 501                // overflow
 1502                val = 0x7FF0000000000000;
 1503            }
 504            else
 1505            {
 506                // normal postive exponent case
 1507                val = ((ulong)exp << 52) + ((val >> 11) & 0x000FFFFFFFFFFFFF);
 1508            }
 509
 1510            if (number.IsNegative)
 1511                val |= 0x8000000000000000;
 512
 1513            return *(double*)&val;
 1514        }
 515
 516        private static class DoubleHelper
 517        {
 518            public static unsafe uint Exponent(double d)
 1519            {
 1520                return (*((uint*)&d + 1) >> 20) & 0x000007ff;
 1521            }
 522
 523            public static unsafe ulong Mantissa(double d)
 1524            {
 1525                return (*((uint*)&d)) | ((ulong)(*((uint*)&d + 1) & 0x000fffff) << 32);
 1526            }
 527
 528            public static unsafe bool Sign(double d)
 529            {
 530                return (*((uint*)&d + 1) >> 31) != 0;
 531            }
 532        }
 533    }
 534}