| | | 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 | | using System.Diagnostics; |
| | | 6 | | using System.Buffers.Text; |
| | | 7 | | |
| | | 8 | | #if !netstandard |
| | | 9 | | using Internal.Runtime.CompilerServices; |
| | | 10 | | #else |
| | | 11 | | using 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 | | |
| | | 18 | | namespace 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) |
| | 1 | 26 | | { |
| | 1 | 27 | | double d = NumberToDouble(ref number); |
| | | 28 | | |
| | 1 | 29 | | uint e = DoubleHelper.Exponent(d); |
| | 1 | 30 | | ulong m = DoubleHelper.Mantissa(d); |
| | 1 | 31 | | if (e == 0x7FF) |
| | 1 | 32 | | { |
| | 1 | 33 | | value = default; |
| | 1 | 34 | | return false; |
| | | 35 | | } |
| | | 36 | | |
| | 1 | 37 | | if (e == 0 && m == 0) |
| | 1 | 38 | | { |
| | 1 | 39 | | d = 0; |
| | 1 | 40 | | } |
| | | 41 | | |
| | 1 | 42 | | value = d; |
| | | 43 | | |
| | 1 | 44 | | return true; |
| | 1 | 45 | | } |
| | | 46 | | |
| | | 47 | | public static unsafe bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value) |
| | 1 | 48 | | { |
| | 1 | 49 | | MutableDecimal d = new MutableDecimal(); |
| | | 50 | | |
| | 1 | 51 | | byte* p = number.UnsafeDigits; |
| | 1 | 52 | | int e = number.Scale; |
| | 1 | 53 | | if (*p == 0) |
| | 1 | 54 | | { |
| | | 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. |
| | 1 | 57 | | if (e > 0) |
| | 0 | 58 | | { |
| | 0 | 59 | | e = 0; |
| | 0 | 60 | | } |
| | 1 | 61 | | } |
| | | 62 | | else |
| | 1 | 63 | | { |
| | 1 | 64 | | if (e > DECIMAL_PRECISION) |
| | 1 | 65 | | return false; |
| | | 66 | | |
| | 1 | 67 | | while (((e > 0) || ((*p != 0) && (e > -28))) && |
| | 1 | 68 | | ((d.High < 0x19999999) || ((d.High == 0x19999999) && |
| | 1 | 69 | | ((d.Mid < 0x99999999) || ((d.Mid == 0x99999999) && |
| | 1 | 70 | | ((d.Low < 0x99999999) || ((d.Low == 0x999999 |
| | 1 | 71 | | (*p <= '5')))))))) |
| | 1 | 72 | | { |
| | 1 | 73 | | DecimalDecCalc.DecMul10(ref d); |
| | 1 | 74 | | if (*p != 0) |
| | 1 | 75 | | DecimalDecCalc.DecAddInt32(ref d, (uint)(*p++ - '0')); |
| | 1 | 76 | | e--; |
| | 1 | 77 | | } |
| | | 78 | | |
| | 1 | 79 | | if (*p++ >= '5') |
| | 1 | 80 | | { |
| | 1 | 81 | | bool round = true; |
| | 1 | 82 | | if ((*(p - 1) == '5') && ((*(p - 2) % 2) == 0)) |
| | 1 | 83 | | { |
| | | 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. |
| | 1 | 86 | | int count = 20; // Look at the next 20 digits to check to round |
| | 1 | 87 | | while ((*p == '0') && (count != 0)) |
| | 1 | 88 | | { |
| | 1 | 89 | | p++; |
| | 1 | 90 | | count--; |
| | 1 | 91 | | } |
| | 1 | 92 | | if ((*p == '\0') || (count == 0)) |
| | 1 | 93 | | round = false;// Do nothing |
| | 1 | 94 | | } |
| | | 95 | | |
| | 1 | 96 | | if (round) |
| | 1 | 97 | | { |
| | 1 | 98 | | DecimalDecCalc.DecAddInt32(ref d, 1); |
| | 1 | 99 | | if ((d.High | d.Mid | d.Low) == 0) |
| | 1 | 100 | | { |
| | | 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 | | |
| | 1 | 114 | | d.High = 0x19999999; |
| | 1 | 115 | | d.Mid = 0x99999999; |
| | 1 | 116 | | d.Low = 0x9999999A; |
| | 1 | 117 | | e++; |
| | 1 | 118 | | } |
| | 1 | 119 | | } |
| | 1 | 120 | | } |
| | 1 | 121 | | } |
| | | 122 | | |
| | 1 | 123 | | if (e > 0) |
| | 1 | 124 | | return false; // Rounding may have caused its own overflow. For example, parsing "0.79228162514264337593 |
| | | 125 | | |
| | 1 | 126 | | if (e <= -DECIMAL_PRECISION) |
| | 1 | 127 | | { |
| | | 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. |
| | 1 | 130 | | d.High = 0; |
| | 1 | 131 | | d.Low = 0; |
| | 1 | 132 | | d.Mid = 0; |
| | 1 | 133 | | d.Scale = DECIMAL_PRECISION - 1; |
| | 1 | 134 | | } |
| | | 135 | | else |
| | 1 | 136 | | { |
| | 1 | 137 | | d.Scale = -e; |
| | 1 | 138 | | } |
| | 1 | 139 | | d.IsNegative = number.IsNegative; |
| | | 140 | | |
| | 1 | 141 | | value = Unsafe.As<MutableDecimal, decimal>(ref d); |
| | 1 | 142 | | return true; |
| | 1 | 143 | | } |
| | | 144 | | |
| | | 145 | | public static void DecimalToNumber(decimal value, ref NumberBuffer number) |
| | 1 | 146 | | { |
| | 1 | 147 | | ref MutableDecimal d = ref Unsafe.As<decimal, MutableDecimal>(ref value); |
| | | 148 | | |
| | 1 | 149 | | Span<byte> buffer = number.Digits; |
| | 1 | 150 | | number.IsNegative = d.IsNegative; |
| | | 151 | | |
| | 1 | 152 | | 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.) |
| | 1 | 156 | | while (d.Mid != 0 | d.High != 0) |
| | 1 | 157 | | { |
| | 1 | 158 | | uint modulo1E9 = DecimalDecCalc.DecDivMod1E9(ref d); |
| | 3 | 159 | | for (int digitCount = 0; digitCount < 9; digitCount++) |
| | 1 | 160 | | { |
| | 1 | 161 | | buffer[--index] = (byte)(modulo1E9 % 10 + '0'); |
| | 1 | 162 | | modulo1E9 /= 10; |
| | 1 | 163 | | } |
| | 1 | 164 | | } |
| | | 165 | | |
| | | 166 | | // We've finally whittled the decimal down to uint.MaxValue or less. Write the remaining digits but make sur |
| | 1 | 167 | | uint remainder = d.Low; |
| | 1 | 168 | | while (remainder != 0) |
| | 1 | 169 | | { |
| | 1 | 170 | | buffer[--index] = (byte)(remainder % 10 + '0'); |
| | 1 | 171 | | remainder /= 10; |
| | 1 | 172 | | } |
| | | 173 | | |
| | 1 | 174 | | int i = DECIMAL_PRECISION - index; |
| | 1 | 175 | | number.Scale = i - d.Scale; |
| | | 176 | | |
| | | 177 | | // Move the result from the end of the buffer to the beginning where we need it. |
| | 1 | 178 | | Span<byte> dst = number.Digits; |
| | 1 | 179 | | int dstIndex = 0; |
| | 1 | 180 | | while (--i >= 0) |
| | 1 | 181 | | { |
| | 1 | 182 | | dst[dstIndex++] = buffer[index++]; |
| | 1 | 183 | | } |
| | 1 | 184 | | dst[dstIndex] = 0; |
| | | 185 | | |
| | 1 | 186 | | number.CheckConsistency(); |
| | 1 | 187 | | } |
| | | 188 | | |
| | | 189 | | // |
| | | 190 | | // get 32-bit integer from at most 9 digits |
| | | 191 | | // |
| | | 192 | | private static uint DigitsToInt(ReadOnlySpan<byte> digits, int count) |
| | 1 | 193 | | { |
| | 1 | 194 | | bool success = Utf8Parser.TryParse(digits.Slice(0, count), out uint value, out int bytesConsumed, 'D'); |
| | 1 | 195 | | Debug.Assert(success); // This is only called on the contents of a trusted Number structure. |
| | 1 | 196 | | return value; |
| | 1 | 197 | | } |
| | | 198 | | |
| | | 199 | | // |
| | | 200 | | // helper to multiply two 32-bit uints |
| | | 201 | | // |
| | | 202 | | private static ulong Mul32x32To64(uint a, uint b) |
| | 1 | 203 | | { |
| | 1 | 204 | | return a * (ulong)b; |
| | 1 | 205 | | } |
| | | 206 | | |
| | | 207 | | // |
| | | 208 | | // multiply two numbers in the internal integer representation |
| | | 209 | | // |
| | | 210 | | private static ulong Mul64Lossy(ulong a, ulong b, ref int pexp) |
| | 1 | 211 | | { |
| | | 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 |
| | 1 | 215 | | ulong val = Mul32x32To64((uint)(a >> 32), (uint)(b >> 32)) + |
| | 1 | 216 | | (Mul32x32To64((uint)(a >> 32), (uint)(b)) >> 32) + |
| | 1 | 217 | | (Mul32x32To64((uint)(a), (uint)(b >> 32)) >> 32); |
| | | 218 | | |
| | | 219 | | // normalize |
| | 1 | 220 | | if ((val & 0x8000000000000000) == 0) |
| | 1 | 221 | | { |
| | 1 | 222 | | val <<= 1; |
| | 1 | 223 | | pexp -= 1; |
| | 1 | 224 | | } |
| | | 225 | | |
| | 1 | 226 | | return val; |
| | 1 | 227 | | } |
| | | 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 | | |
| | 1 | 235 | | private static readonly ulong[] s_rgval64Power10 = |
| | 1 | 236 | | { |
| | 1 | 237 | | // powers of 10 |
| | 1 | 238 | | /*1*/ 0xa000000000000000, |
| | 1 | 239 | | /*2*/ 0xc800000000000000, |
| | 1 | 240 | | /*3*/ 0xfa00000000000000, |
| | 1 | 241 | | /*4*/ 0x9c40000000000000, |
| | 1 | 242 | | /*5*/ 0xc350000000000000, |
| | 1 | 243 | | /*6*/ 0xf424000000000000, |
| | 1 | 244 | | /*7*/ 0x9896800000000000, |
| | 1 | 245 | | /*8*/ 0xbebc200000000000, |
| | 1 | 246 | | /*9*/ 0xee6b280000000000, |
| | 1 | 247 | | /*10*/ 0x9502f90000000000, |
| | 1 | 248 | | /*11*/ 0xba43b74000000000, |
| | 1 | 249 | | /*12*/ 0xe8d4a51000000000, |
| | 1 | 250 | | /*13*/ 0x9184e72a00000000, |
| | 1 | 251 | | /*14*/ 0xb5e620f480000000, |
| | 1 | 252 | | /*15*/ 0xe35fa931a0000000, |
| | 1 | 253 | | |
| | 1 | 254 | | // powers of 0.1 |
| | 1 | 255 | | /*1*/ 0xcccccccccccccccd, |
| | 1 | 256 | | /*2*/ 0xa3d70a3d70a3d70b, |
| | 1 | 257 | | /*3*/ 0x83126e978d4fdf3c, |
| | 1 | 258 | | /*4*/ 0xd1b71758e219652e, |
| | 1 | 259 | | /*5*/ 0xa7c5ac471b478425, |
| | 1 | 260 | | /*6*/ 0x8637bd05af6c69b7, |
| | 1 | 261 | | /*7*/ 0xd6bf94d5e57a42be, |
| | 1 | 262 | | /*8*/ 0xabcc77118461ceff, |
| | 1 | 263 | | /*9*/ 0x89705f4136b4a599, |
| | 1 | 264 | | /*10*/ 0xdbe6fecebdedd5c2, |
| | 1 | 265 | | /*11*/ 0xafebff0bcb24ab02, |
| | 1 | 266 | | /*12*/ 0x8cbccc096f5088cf, |
| | 1 | 267 | | /*13*/ 0xe12e13424bb40e18, |
| | 1 | 268 | | /*14*/ 0xb424dc35095cd813, |
| | 1 | 269 | | /*15*/ 0x901d7cf73ab0acdc, |
| | 1 | 270 | | }; |
| | | 271 | | |
| | 1 | 272 | | private static readonly sbyte[] s_rgexp64Power10 = |
| | 1 | 273 | | { |
| | 1 | 274 | | // exponents for both powers of 10 and 0.1 |
| | 1 | 275 | | /*1*/ 4, |
| | 1 | 276 | | /*2*/ 7, |
| | 1 | 277 | | /*3*/ 10, |
| | 1 | 278 | | /*4*/ 14, |
| | 1 | 279 | | /*5*/ 17, |
| | 1 | 280 | | /*6*/ 20, |
| | 1 | 281 | | /*7*/ 24, |
| | 1 | 282 | | /*8*/ 27, |
| | 1 | 283 | | /*9*/ 30, |
| | 1 | 284 | | /*10*/ 34, |
| | 1 | 285 | | /*11*/ 37, |
| | 1 | 286 | | /*12*/ 40, |
| | 1 | 287 | | /*13*/ 44, |
| | 1 | 288 | | /*14*/ 47, |
| | 1 | 289 | | /*15*/ 50, |
| | 1 | 290 | | }; |
| | | 291 | | |
| | 1 | 292 | | private static readonly ulong[] s_rgval64Power10By16 = |
| | 1 | 293 | | { |
| | 1 | 294 | | // powers of 10^16 |
| | 1 | 295 | | /*1*/ 0x8e1bc9bf04000000, |
| | 1 | 296 | | /*2*/ 0x9dc5ada82b70b59e, |
| | 1 | 297 | | /*3*/ 0xaf298d050e4395d6, |
| | 1 | 298 | | /*4*/ 0xc2781f49ffcfa6d4, |
| | 1 | 299 | | /*5*/ 0xd7e77a8f87daf7fa, |
| | 1 | 300 | | /*6*/ 0xefb3ab16c59b14a0, |
| | 1 | 301 | | /*7*/ 0x850fadc09923329c, |
| | 1 | 302 | | /*8*/ 0x93ba47c980e98cde, |
| | 1 | 303 | | /*9*/ 0xa402b9c5a8d3a6e6, |
| | 1 | 304 | | /*10*/ 0xb616a12b7fe617a8, |
| | 1 | 305 | | /*11*/ 0xca28a291859bbf90, |
| | 1 | 306 | | /*12*/ 0xe070f78d39275566, |
| | 1 | 307 | | /*13*/ 0xf92e0c3537826140, |
| | 1 | 308 | | /*14*/ 0x8a5296ffe33cc92c, |
| | 1 | 309 | | /*15*/ 0x9991a6f3d6bf1762, |
| | 1 | 310 | | /*16*/ 0xaa7eebfb9df9de8a, |
| | 1 | 311 | | /*17*/ 0xbd49d14aa79dbc7e, |
| | 1 | 312 | | /*18*/ 0xd226fc195c6a2f88, |
| | 1 | 313 | | /*19*/ 0xe950df20247c83f8, |
| | 1 | 314 | | /*20*/ 0x81842f29f2cce373, |
| | 1 | 315 | | /*21*/ 0x8fcac257558ee4e2, |
| | 1 | 316 | | |
| | 1 | 317 | | // powers of 0.1^16 |
| | 1 | 318 | | /*1*/ 0xe69594bec44de160, |
| | 1 | 319 | | /*2*/ 0xcfb11ead453994c3, |
| | 1 | 320 | | /*3*/ 0xbb127c53b17ec165, |
| | 1 | 321 | | /*4*/ 0xa87fea27a539e9b3, |
| | 1 | 322 | | /*5*/ 0x97c560ba6b0919b5, |
| | 1 | 323 | | /*6*/ 0x88b402f7fd7553ab, |
| | 1 | 324 | | /*7*/ 0xf64335bcf065d3a0, |
| | 1 | 325 | | /*8*/ 0xddd0467c64bce4c4, |
| | 1 | 326 | | /*9*/ 0xc7caba6e7c5382ed, |
| | 1 | 327 | | /*10*/ 0xb3f4e093db73a0b7, |
| | 1 | 328 | | /*11*/ 0xa21727db38cb0053, |
| | 1 | 329 | | /*12*/ 0x91ff83775423cc29, |
| | 1 | 330 | | /*13*/ 0x8380dea93da4bc82, |
| | 1 | 331 | | /*14*/ 0xece53cec4a314f00, |
| | 1 | 332 | | /*15*/ 0xd5605fcdcf32e217, |
| | 1 | 333 | | /*16*/ 0xc0314325637a1978, |
| | 1 | 334 | | /*17*/ 0xad1c8eab5ee43ba2, |
| | 1 | 335 | | /*18*/ 0x9becce62836ac5b0, |
| | 1 | 336 | | /*19*/ 0x8c71dcd9ba0b495c, |
| | 1 | 337 | | /*20*/ 0xfd00b89747823938, |
| | 1 | 338 | | /*21*/ 0xe3e27a444d8d991a, |
| | 1 | 339 | | }; |
| | | 340 | | |
| | 1 | 341 | | private static readonly short[] s_rgexp64Power10By16 = |
| | 1 | 342 | | { |
| | 1 | 343 | | // exponents for both powers of 10^16 and 0.1^16 |
| | 1 | 344 | | /*1*/ 54, |
| | 1 | 345 | | /*2*/ 107, |
| | 1 | 346 | | /*3*/ 160, |
| | 1 | 347 | | /*4*/ 213, |
| | 1 | 348 | | /*5*/ 266, |
| | 1 | 349 | | /*6*/ 319, |
| | 1 | 350 | | /*7*/ 373, |
| | 1 | 351 | | /*8*/ 426, |
| | 1 | 352 | | /*9*/ 479, |
| | 1 | 353 | | /*10*/ 532, |
| | 1 | 354 | | /*11*/ 585, |
| | 1 | 355 | | /*12*/ 638, |
| | 1 | 356 | | /*13*/ 691, |
| | 1 | 357 | | /*14*/ 745, |
| | 1 | 358 | | /*15*/ 798, |
| | 1 | 359 | | /*16*/ 851, |
| | 1 | 360 | | /*17*/ 904, |
| | 1 | 361 | | /*18*/ 957, |
| | 1 | 362 | | /*19*/ 1010, |
| | 1 | 363 | | /*20*/ 1064, |
| | 1 | 364 | | /*21*/ 1117, |
| | 1 | 365 | | }; |
| | | 366 | | |
| | | 367 | | private static int abs(int value) |
| | 1 | 368 | | { |
| | 1 | 369 | | if (value < 0) |
| | 1 | 370 | | return -value; |
| | 1 | 371 | | return value; |
| | 1 | 372 | | } |
| | | 373 | | |
| | | 374 | | private static unsafe double NumberToDouble(ref NumberBuffer number) |
| | 1 | 375 | | { |
| | | 376 | | ulong val; |
| | | 377 | | int exp; |
| | 1 | 378 | | ReadOnlySpan<byte> src = number.Digits; |
| | | 379 | | int remaining; |
| | | 380 | | int total; |
| | | 381 | | int count; |
| | | 382 | | int scale; |
| | | 383 | | int absscale; |
| | | 384 | | int index; |
| | 1 | 385 | | int srcIndex = 0; |
| | | 386 | | |
| | 1 | 387 | | total = number.NumDigits; |
| | 1 | 388 | | remaining = total; |
| | | 389 | | |
| | | 390 | | // skip the leading zeros |
| | 1 | 391 | | while (src[srcIndex] == '0') |
| | 0 | 392 | | { |
| | 0 | 393 | | remaining--; |
| | 0 | 394 | | srcIndex++; |
| | 0 | 395 | | } |
| | | 396 | | |
| | 1 | 397 | | if (remaining == 0) |
| | 1 | 398 | | return 0; |
| | | 399 | | |
| | 1 | 400 | | count = Math.Min(remaining, 9); |
| | 1 | 401 | | remaining -= count; |
| | 1 | 402 | | val = DigitsToInt(src, count); |
| | | 403 | | |
| | 1 | 404 | | if (remaining > 0) |
| | 1 | 405 | | { |
| | 1 | 406 | | count = Math.Min(remaining, 9); |
| | 1 | 407 | | remaining -= count; |
| | | 408 | | |
| | | 409 | | // get the denormalized power of 10 |
| | 1 | 410 | | uint mult = (uint)(s_rgval64Power10[count - 1] >> (64 - s_rgexp64Power10[count - 1])); |
| | 1 | 411 | | val = Mul32x32To64((uint)val, mult) + DigitsToInt(src.Slice(9), count); |
| | 1 | 412 | | } |
| | | 413 | | |
| | 1 | 414 | | scale = number.Scale - (total - remaining); |
| | 1 | 415 | | absscale = abs(scale); |
| | 1 | 416 | | if (absscale >= 22 * 16) |
| | 1 | 417 | | { |
| | | 418 | | // overflow / underflow |
| | 1 | 419 | | ulong result = (scale > 0) ? 0x7FF0000000000000 : 0ul; |
| | 1 | 420 | | if (number.IsNegative) |
| | 1 | 421 | | result |= 0x8000000000000000; |
| | 1 | 422 | | return *(double*)&result; |
| | | 423 | | } |
| | | 424 | | |
| | 1 | 425 | | exp = 64; |
| | | 426 | | |
| | | 427 | | // normalize the mantissa |
| | 1 | 428 | | if ((val & 0xFFFFFFFF00000000) == 0) |
| | 4 | 429 | | { val <<= 32; exp -= 32; } |
| | 1 | 430 | | if ((val & 0xFFFF000000000000) == 0) |
| | 4 | 431 | | { val <<= 16; exp -= 16; } |
| | 1 | 432 | | if ((val & 0xFF00000000000000) == 0) |
| | 4 | 433 | | { val <<= 8; exp -= 8; } |
| | 1 | 434 | | if ((val & 0xF000000000000000) == 0) |
| | 4 | 435 | | { val <<= 4; exp -= 4; } |
| | 1 | 436 | | if ((val & 0xC000000000000000) == 0) |
| | 4 | 437 | | { val <<= 2; exp -= 2; } |
| | 1 | 438 | | if ((val & 0x8000000000000000) == 0) |
| | 4 | 439 | | { val <<= 1; exp -= 1; } |
| | | 440 | | |
| | 1 | 441 | | index = absscale & 15; |
| | 1 | 442 | | if (index != 0) |
| | 1 | 443 | | { |
| | 1 | 444 | | int multexp = s_rgexp64Power10[index - 1]; |
| | | 445 | | // the exponents are shared between the inverted and regular table |
| | 1 | 446 | | exp += (scale < 0) ? (-multexp + 1) : multexp; |
| | | 447 | | |
| | 1 | 448 | | ulong multval = s_rgval64Power10[index + ((scale < 0) ? 15 : 0) - 1]; |
| | 1 | 449 | | val = Mul64Lossy(val, multval, ref exp); |
| | 1 | 450 | | } |
| | | 451 | | |
| | 1 | 452 | | index = absscale >> 4; |
| | 1 | 453 | | if (index != 0) |
| | 1 | 454 | | { |
| | 1 | 455 | | int multexp = s_rgexp64Power10By16[index - 1]; |
| | | 456 | | // the exponents are shared between the inverted and regular table |
| | 1 | 457 | | exp += (scale < 0) ? (-multexp + 1) : multexp; |
| | | 458 | | |
| | 1 | 459 | | ulong multval = s_rgval64Power10By16[index + ((scale < 0) ? 21 : 0) - 1]; |
| | 1 | 460 | | val = Mul64Lossy(val, multval, ref exp); |
| | 1 | 461 | | } |
| | | 462 | | |
| | | 463 | | // round & scale down |
| | 1 | 464 | | if (((int)val & (1 << 10)) != 0) |
| | 1 | 465 | | { |
| | | 466 | | // IEEE round to even |
| | 1 | 467 | | ulong tmp = val + ((1 << 10) - 1) + (ulong)(((int)val >> 11) & 1); |
| | 1 | 468 | | if (tmp < val) |
| | 1 | 469 | | { |
| | | 470 | | // overflow |
| | 1 | 471 | | tmp = (tmp >> 1) | 0x8000000000000000; |
| | 1 | 472 | | exp += 1; |
| | 1 | 473 | | } |
| | 1 | 474 | | val = tmp; |
| | 1 | 475 | | } |
| | | 476 | | |
| | | 477 | | // return the exponent to a biased state |
| | 1 | 478 | | exp += 0x3FE; |
| | | 479 | | |
| | | 480 | | // handle overflow, underflow, "Epsilon - 1/2 Epsilon", denormalized, and the normal case |
| | 1 | 481 | | if (exp <= 0) |
| | 1 | 482 | | { |
| | 1 | 483 | | if (exp == -52 && (val >= 0x8000000000000058)) |
| | 1 | 484 | | { |
| | | 485 | | // round X where {Epsilon > X >= 2.470328229206232730000000E-324} up to Epsilon (instead of down to |
| | 1 | 486 | | val = 0x0000000000000001; |
| | 1 | 487 | | } |
| | 1 | 488 | | else if (exp <= -52) |
| | 1 | 489 | | { |
| | | 490 | | // underflow |
| | 1 | 491 | | val = 0; |
| | 1 | 492 | | } |
| | | 493 | | else |
| | 1 | 494 | | { |
| | | 495 | | // denormalized |
| | 1 | 496 | | val >>= (-exp + 11 + 1); |
| | 1 | 497 | | } |
| | 1 | 498 | | } |
| | 1 | 499 | | else if (exp >= 0x7FF) |
| | 1 | 500 | | { |
| | | 501 | | // overflow |
| | 1 | 502 | | val = 0x7FF0000000000000; |
| | 1 | 503 | | } |
| | | 504 | | else |
| | 1 | 505 | | { |
| | | 506 | | // normal postive exponent case |
| | 1 | 507 | | val = ((ulong)exp << 52) + ((val >> 11) & 0x000FFFFFFFFFFFFF); |
| | 1 | 508 | | } |
| | | 509 | | |
| | 1 | 510 | | if (number.IsNegative) |
| | 1 | 511 | | val |= 0x8000000000000000; |
| | | 512 | | |
| | 1 | 513 | | return *(double*)&val; |
| | 1 | 514 | | } |
| | | 515 | | |
| | | 516 | | private static class DoubleHelper |
| | | 517 | | { |
| | | 518 | | public static unsafe uint Exponent(double d) |
| | 1 | 519 | | { |
| | 1 | 520 | | return (*((uint*)&d + 1) >> 20) & 0x000007ff; |
| | 1 | 521 | | } |
| | | 522 | | |
| | | 523 | | public static unsafe ulong Mantissa(double d) |
| | 1 | 524 | | { |
| | 1 | 525 | | return (*((uint*)&d)) | ((ulong)(*((uint*)&d + 1) & 0x000fffff) << 32); |
| | 1 | 526 | | } |
| | | 527 | | |
| | | 528 | | public static unsafe bool Sign(double d) |
| | | 529 | | { |
| | | 530 | | return (*((uint*)&d + 1) >> 31) != 0; |
| | | 531 | | } |
| | | 532 | | } |
| | | 533 | | } |
| | | 534 | | } |