A game about forced loneliness, made by TACStudios
at master 1924 lines 84 kB view raw
1using System; 2using System.Runtime.CompilerServices; 3using System.Runtime.InteropServices; 4 5namespace Unity.Burst 6{ 7#if BURST_COMPILER_SHARED 8 internal static partial class BurstStringInternal 9#else 10 internal static partial class BurstString 11#endif 12 { 13 // This file provides an implementation for formatting floating point numbers that is compatible 14 // with Burst 15 16 // ------------------------------------------------------------------------------ 17 // Part of code translated to C# from http://www.ryanjuckett.com/programming/printing-floating-point-numbers 18 // with the following license: 19 /****************************************************************************** 20 Copyright (c) 2014 Ryan Juckett 21 http://www.ryanjuckett.com/ 22 23 This software is provided 'as-is', without any express or implied 24 warranty. In no event will the authors be held liable for any damages 25 arising from the use of this software. 26 27 Permission is granted to anyone to use this software for any purpose, 28 including commercial applications, and to alter it and redistribute it 29 freely, subject to the following restrictions: 30 31 1. The origin of this software must not be misrepresented; you must not 32 claim that you wrote the original software. If you use this software 33 in a product, an acknowledgment in the product documentation would be 34 appreciated but is not required. 35 36 2. Altered source versions must be plainly marked as such, and must not be 37 misrepresented as being the original software. 38 39 3. This notice may not be removed or altered from any source 40 distribution. 41 ******************************************************************************/ 42 43 //****************************************************************************** 44 // Get the log base 2 of a 32-bit unsigned integer. 45 // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup 46 //****************************************************************************** 47 private static readonly byte[] logTable = new byte[256] 48 { 49 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 50 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 51 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 52 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 53 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 54 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 55 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 56 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 57 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 58 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 59 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 60 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 61 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 62 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 63 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 64 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 65 }; 66 67 private static uint LogBase2(uint val) 68 { 69 uint temp; 70 temp = val >> 24; 71 if (temp != 0) 72 return (uint)(24 + logTable[(int)temp]); 73 74 temp = val >> 16; 75 if (temp != 0) 76 return (uint)(16 + logTable[temp]); 77 78 temp = val >> 8; 79 if (temp != 0) 80 return (uint)(8 + logTable[temp]); 81 82 return logTable[val]; 83 } 84 85 //****************************************************************************** 86 // This structure stores a high precision unsigned integer. It uses a buffer 87 // of 32 bit integer blocks along with a length. The lowest bits of the integer 88 // are stored at the start of the buffer and the length is set to the minimum 89 // value that contains the integer. Thus, there are never any zero blocks at the 90 // end of the buffer. 91 //****************************************************************************** 92 public unsafe struct tBigInt 93 { 94 //****************************************************************************** 95 // Maximum number of 32 bit blocks needed in high precision arithmetic 96 // to print out 64 bit IEEE floating point values. 97 //****************************************************************************** 98 const int c_BigInt_MaxBlocks = 35; 99 100 //// Copy integer 101 //tBigInt & operator=(tBigInt &rhs) 102 //{ 103 // uint length = rhs.m_length; 104 // uint* pLhsCur = m_blocks; 105 // for (uint* pRhsCur = rhs.m_blocks, *pRhsEnd = pRhsCur + length; 106 // pRhsCur != pRhsEnd; 107 // ++pLhsCur, ++pRhsCur) 108 // { 109 // *pLhsCur = *pRhsCur; 110 // } 111 // m_length = length; 112 // return *this; 113 //} 114 115 // Data accessors 116 public int GetLength() { return m_length; } 117 public uint GetBlock(int idx) { return m_blocks[idx]; } 118 119 // Zero helper functions 120 public void SetZero() { m_length = 0; } 121 public bool IsZero() { return m_length == 0; } 122 123 // Basic type accessors 124 public void SetU64(ulong val) 125 { 126 if (val > 0xFFFFFFFF) 127 { 128 m_blocks[0] = (uint)(val & 0xFFFFFFFF); 129 m_blocks[1] = (uint)(val >> 32 & 0xFFFFFFFF); 130 m_length = 2; 131 } 132 else if (val != 0) 133 { 134 m_blocks[0] = (uint)(val & 0xFFFFFFFF); 135 m_length = 1; 136 } 137 else 138 { 139 m_length = 0; 140 } 141 } 142 143 public void SetU32(uint val) 144 { 145 if (val != 0) 146 { 147 m_blocks[0] = val; 148 m_length = (val != 0) ? 1 : 0; 149 } 150 else 151 { 152 m_length = 0; 153 } 154 } 155 156 public uint GetU32() { return (m_length == 0) ? 0 : m_blocks[0]; } 157 158 // Member data 159 public int m_length; 160 public fixed uint m_blocks[c_BigInt_MaxBlocks]; 161 } 162 163 //****************************************************************************** 164 // Returns 0 if (lhs = rhs), negative if (lhs < rhs), positive if (lhs > rhs) 165 //****************************************************************************** 166 private static unsafe int BigInt_Compare(in tBigInt lhs, in tBigInt rhs) 167 { 168 // A bigger length implies a bigger number. 169 int lengthDiff = lhs.m_length - rhs.m_length; 170 if (lengthDiff != 0) 171 return lengthDiff; 172 173 // Compare blocks one by one from high to low. 174 for (int i = (int)lhs.m_length - 1; i >= 0; --i) 175 { 176 if (lhs.m_blocks[i] == rhs.m_blocks[i]) 177 continue; 178 else if (lhs.m_blocks[i] > rhs.m_blocks[i]) 179 return 1; 180 else 181 return -1; 182 } 183 184 // no blocks differed 185 return 0; 186 } 187 188 //****************************************************************************** 189 // result = lhs + rhs 190 //****************************************************************************** 191 private static unsafe void BigInt_Add(out tBigInt pResult, in tBigInt lhs, in tBigInt rhs) 192 { 193 if (lhs.m_length < rhs.m_length) 194 { 195 BigInt_Add_internal(out pResult, rhs, lhs); 196 } 197 else 198 { 199 BigInt_Add_internal(out pResult, lhs, rhs); 200 } 201 } 202 private static unsafe void BigInt_Add_internal(out tBigInt pResult, in tBigInt pLarge, in tBigInt pSmall) 203 { 204 int largeLen = pLarge.m_length; 205 int smallLen = pSmall.m_length; 206 207 // The output will be at least as long as the largest input 208 pResult.m_length = largeLen; 209 210 // Add each block and add carry the overflow to the next block 211 ulong carry = 0; 212 fixed (uint * pLargeCur1 = pLarge.m_blocks) 213 fixed (uint * pSmallCur1 = pSmall.m_blocks) 214 fixed (uint * pResultCur1 = pResult.m_blocks) 215 { 216 uint* pLargeCur = pLargeCur1; 217 uint* pSmallCur = pSmallCur1; 218 uint* pResultCur = pResultCur1; 219 uint* pLargeEnd = pLargeCur + largeLen; 220 uint* pSmallEnd = pSmallCur + smallLen; 221 222 while (pSmallCur != pSmallEnd) 223 { 224 ulong sum = carry + (ulong) (*pLargeCur) + (ulong) (*pSmallCur); 225 carry = sum >> 32; 226 (*pResultCur) = (uint)(sum & 0xFFFFFFFF); 227 ++pLargeCur; 228 ++pSmallCur; 229 ++pResultCur; 230 } 231 232 // Add the carry to any blocks that only exist in the large operand 233 while (pLargeCur != pLargeEnd) 234 { 235 ulong sum = carry + (ulong) (*pLargeCur); 236 carry = sum >> 32; 237 (*pResultCur) = (uint)(sum & 0xFFFFFFFF); 238 ++pLargeCur; 239 ++pResultCur; 240 } 241 242 // If there's still a carry, append a new block 243 if (carry != 0) 244 { 245 //RJ_ASSERT(carry == 1); 246 //RJ_ASSERT((uint)(pResultCur - pResult.m_blocks) == largeLen && (largeLen < c_BigInt_MaxBlocks)); 247 *pResultCur = 1; 248 pResult.m_length = largeLen + 1; 249 } 250 else 251 { 252 pResult.m_length = largeLen; 253 } 254 } 255 } 256 257 //****************************************************************************** 258 // result = lhs * rhs 259 //****************************************************************************** 260 private static unsafe void BigInt_Multiply(out tBigInt pResult, in tBigInt lhs, in tBigInt rhs) 261 { 262 if (lhs.m_length < rhs.m_length) 263 { 264 BigInt_Multiply_internal(out pResult, rhs, lhs); 265 } 266 else 267 { 268 BigInt_Multiply_internal(out pResult, lhs, rhs); 269 } 270 } 271 272 private static unsafe void BigInt_Multiply_internal(out tBigInt pResult, in tBigInt pLarge, in tBigInt pSmall) 273 { 274 // set the maximum possible result length 275 int maxResultLen = pLarge.m_length + pSmall.m_length; 276 // RJ_ASSERT( maxResultLen <= c_BigInt_MaxBlocks ); 277 278 // clear the result data 279 // uint * pCur = pResult.m_blocks, *pEnd = pCur + maxResultLen; pCur != pEnd; ++pCur 280 for (int i = 0; i < maxResultLen; i++) 281 pResult.m_blocks[i] = 0; 282 283 // perform standard long multiplication 284 fixed (uint *pLargeBeg1 = pLarge.m_blocks) 285 { 286 uint* pLargeBeg = pLargeBeg1; 287 uint* pLargeEnd = pLargeBeg + pLarge.m_length; 288 289 // for each small block 290 fixed (uint* pResultStart1 = pResult.m_blocks) 291 fixed (uint* pSmallCur1 = pSmall.m_blocks) 292 { 293 uint* pSmallCur = pSmallCur1; 294 uint* pSmallEnd = pSmallCur + pSmall.m_length; 295 uint* pResultStart = pResultStart1; 296 for (; pSmallCur != pSmallEnd; ++pSmallCur, ++pResultStart) 297 { 298 // if non-zero, multiply against all the large blocks and add into the result 299 uint multiplier = *pSmallCur; 300 if (multiplier != 0) 301 { 302 uint* pLargeCur = pLargeBeg; 303 uint* pResultCur = pResultStart; 304 ulong carry = 0; 305 do 306 { 307 ulong product = (*pResultCur) + (*pLargeCur) * (ulong) multiplier + carry; 308 carry = product >> 32; 309 *pResultCur = (uint)(product & 0xFFFFFFFF); 310 ++pLargeCur; 311 ++pResultCur; 312 } while (pLargeCur != pLargeEnd); 313 314 //RJ_ASSERT(pResultCur < pResult.m_blocks + maxResultLen); 315 *pResultCur = (uint) (carry & 0xFFFFFFFF); 316 } 317 } 318 319 // check if the terminating block has no set bits 320 if (maxResultLen > 0 && pResult.m_blocks[maxResultLen - 1] == 0) 321 pResult.m_length = maxResultLen - 1; 322 else 323 pResult.m_length = maxResultLen; 324 } 325 } 326 } 327 328 //****************************************************************************** 329 // result = lhs * rhs 330 //****************************************************************************** 331 private static unsafe void BigInt_Multiply(out tBigInt pResult, in tBigInt lhs, uint rhs) 332 { 333 // perform long multiplication 334 uint carry = 0; 335 fixed (uint* pResultCur1 = pResult.m_blocks) 336 fixed (uint* pLhsCur1 = lhs.m_blocks) 337 { 338 uint* pResultCur = pResultCur1; 339 uint* pLhsCur = pLhsCur1; 340 uint* pLhsEnd = pLhsCur + lhs.m_length; 341 for (; pLhsCur != pLhsEnd; ++pLhsCur, ++pResultCur) 342 { 343 ulong product = (ulong) (*pLhsCur) * rhs + carry; 344 *pResultCur = (uint) (product & 0xFFFFFFFF); 345 carry = (uint)(product >> 32); 346 } 347 348 // if there is a remaining carry, grow the array 349 if (carry != 0) 350 { 351 // grow the array 352 //RJ_ASSERT(lhs.m_length + 1 <= c_BigInt_MaxBlocks); 353 *pResultCur = (uint) carry; 354 pResult.m_length = lhs.m_length + 1; 355 } 356 else 357 { 358 pResult.m_length = lhs.m_length; 359 } 360 } 361 } 362 363 //****************************************************************************** 364 // result = in * 2 365 //****************************************************************************** 366 private static unsafe void BigInt_Multiply2(out tBigInt pResult, in tBigInt input) 367 { 368 // shift all the blocks by one 369 uint carry = 0; 370 371 fixed (uint* pResultCur1 = pResult.m_blocks) 372 fixed (uint* pLhsCur1 = input.m_blocks) 373 { 374 uint* pResultCur = pResultCur1; 375 uint* pLhsCur = pLhsCur1; 376 uint* pLhsEnd = pLhsCur + input.m_length; 377 for (; pLhsCur != pLhsEnd; ++pLhsCur, ++pResultCur) 378 { 379 uint cur = *pLhsCur; 380 *pResultCur = (cur << 1) | carry; 381 carry = cur >> 31; 382 } 383 384 if (carry != 0) 385 { 386 // grow the array 387 // RJ_ASSERT(input.m_length + 1 <= c_BigInt_MaxBlocks); 388 *pResultCur = carry; 389 pResult.m_length = input.m_length + 1; 390 } 391 else 392 { 393 pResult.m_length = input.m_length; 394 } 395 } 396 } 397 398 //****************************************************************************** 399 // result = result * 2 400 //****************************************************************************** 401 private static unsafe void BigInt_Multiply2(ref tBigInt pResult) 402 { 403 // shift all the blocks by one 404 uint carry = 0; 405 406 fixed (uint* pCur1 = pResult.m_blocks) 407 { 408 uint* pCur = pCur1; 409 uint* pEnd = pCur + pResult.m_length; 410 for (; pCur != pEnd; ++pCur) 411 { 412 uint cur = *pCur; 413 *pCur = (cur << 1) | carry; 414 carry = cur >> 31; 415 } 416 417 if (carry != 0) 418 { 419 // grow the array 420 // RJ_ASSERT(pResult.m_length + 1 <= c_BigInt_MaxBlocks); 421 *pCur = carry; 422 ++pResult.m_length; 423 } 424 } 425 } 426 427 //****************************************************************************** 428 // result = result * 10 429 //****************************************************************************** 430 private static unsafe void BigInt_Multiply10(ref tBigInt pResult) 431 { 432 // multiply all the blocks 433 ulong carry = 0; 434 435 fixed (uint* pCur1 = pResult.m_blocks) 436 { 437 uint* pCur = pCur1; 438 uint* pEnd = pCur + pResult.m_length; 439 for (; pCur != pEnd; ++pCur) 440 { 441 ulong product = (ulong) (*pCur) * 10 + carry; 442 (*pCur) = (uint) (product & 0xFFFFFFFF); 443 carry = product >> 32; 444 } 445 446 if (carry != 0) 447 { 448 // grow the array 449 //RJ_ASSERT(pResult.m_length + 1 <= c_BigInt_MaxBlocks); 450 *pCur = (uint) carry; 451 ++pResult.m_length; 452 } 453 } 454 } 455 456 //****************************************************************************** 457 //****************************************************************************** 458 private static readonly uint[] g_PowerOf10_U32 = new uint[] 459 { 460 1, // 10 ^ 0 461 10, // 10 ^ 1 462 100, // 10 ^ 2 463 1000, // 10 ^ 3 464 10000, // 10 ^ 4 465 100000, // 10 ^ 5 466 1000000, // 10 ^ 6 467 10000000, // 10 ^ 7 468 }; 469 470 //****************************************************************************** 471 // Note: This has a lot of wasted space in the big integer structures of the 472 // early table entries. It wouldn't be terribly hard to make the multiply 473 // function work on integer pointers with an array length instead of 474 // the tBigInt struct which would allow us to store a minimal amount of 475 // data here. 476 //****************************************************************************** 477 private static unsafe tBigInt g_PowerOf10_Big(int i) 478 { 479 tBigInt result; 480 // 10 ^ 8 481 if (i == 0) 482 { 483 // { 1, { 100000000 } }, 484 result.m_length = 1; 485 result.m_blocks[0] = 100000000; 486 } 487 else if (i == 1) 488 { 489 // 10 ^ 16 490 // { 2, { 0x6fc10000, 0x002386f2 } }, 491 result.m_length = 2; 492 result.m_blocks[0] = 0x6fc10000; 493 result.m_blocks[1] = 0x002386f2; 494 } 495 else if (i == 2) 496 { 497 // 10 ^ 32 498 // { 4, { 0x00000000, 0x85acef81, 0x2d6d415b, 0x000004ee, } }, 499 result.m_length = 4; 500 result.m_blocks[0] = 0x00000000; 501 result.m_blocks[1] = 0x85acef81; 502 result.m_blocks[2] = 0x2d6d415b; 503 result.m_blocks[3] = 0x000004ee; 504 } 505 else if (i == 3) 506 { 507 // 10 ^ 64 508 // { 7, { 0x00000000, 0x00000000, 0xbf6a1f01, 0x6e38ed64, 0xdaa797ed, 0xe93ff9f4, 0x00184f03, } }, 509 result.m_length = 7; 510 result.m_blocks[0] = 0x00000000; 511 result.m_blocks[1] = 0x00000000; 512 result.m_blocks[2] = 0xbf6a1f01; 513 result.m_blocks[3] = 0x6e38ed64; 514 result.m_blocks[4] = 0xdaa797ed; 515 result.m_blocks[5] = 0xe93ff9f4; 516 result.m_blocks[6] = 0x00184f03; 517 } 518 else if (i == 4) 519 { 520 // 10 ^ 128 521 //{ 522 // 14, { 523 // 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2e953e01, 0x03df9909, 0x0f1538fd, 524 // 0x2374e42f, 0xd3cff5ec, 0xc404dc08, 0xbccdb0da, 0xa6337f19, 0xe91f2603, 0x0000024e, } 525 //}, 526 result.m_length = 14; 527 result.m_blocks[0] = 0x00000000; 528 result.m_blocks[1] = 0x00000000; 529 result.m_blocks[2] = 0x00000000; 530 result.m_blocks[3] = 0x00000000; 531 result.m_blocks[4] = 0x2e953e01; 532 result.m_blocks[5] = 0x03df9909; 533 result.m_blocks[6] = 0x0f1538fd; 534 result.m_blocks[7] = 0x2374e42f; 535 result.m_blocks[8] = 0xd3cff5ec; 536 result.m_blocks[9] = 0xc404dc08; 537 result.m_blocks[10] = 0xbccdb0da; 538 result.m_blocks[11] = 0xa6337f19; 539 result.m_blocks[12] = 0xe91f2603; 540 result.m_blocks[13] = 0x0000024e; 541 542 } 543 else 544 { 545 // 10 ^ 256 546 //{ 547 // 27, { 548 // 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 549 // 0x00000000, 0x982e7c01, 0xbed3875b, 0xd8d99f72, 0x12152f87, 0x6bde50c6, 0xcf4a6e70, 550 // 0xd595d80f, 0x26b2716e, 0xadc666b0, 0x1d153624, 0x3c42d35a, 0x63ff540e, 0xcc5573c0, 551 // 0x65f9ef17, 0x55bc28f2, 0x80dcc7f7, 0xf46eeddc, 0x5fdcefce, 0x000553f7, 552 // } 553 //} 554 result.m_length = 27; 555 result.m_blocks[0] = 0x00000000; 556 result.m_blocks[1] = 0x00000000; 557 result.m_blocks[2] = 0x00000000; 558 result.m_blocks[3] = 0x00000000; 559 result.m_blocks[4] = 0x00000000; 560 result.m_blocks[5] = 0x00000000; 561 result.m_blocks[6] = 0x00000000; 562 result.m_blocks[7] = 0x00000000; 563 result.m_blocks[8] = 0x982e7c01; 564 result.m_blocks[9] = 0xbed3875b; 565 result.m_blocks[10] = 0xd8d99f72; 566 result.m_blocks[11] = 0x12152f87; 567 result.m_blocks[12] = 0x6bde50c6; 568 result.m_blocks[13] = 0xcf4a6e70; 569 result.m_blocks[14] = 0xd595d80f; 570 result.m_blocks[15] = 0x26b2716e; 571 result.m_blocks[16] = 0xadc666b0; 572 result.m_blocks[17] = 0x1d153624; 573 result.m_blocks[18] = 0x3c42d35a; 574 result.m_blocks[19] = 0x63ff540e; 575 result.m_blocks[20] = 0xcc5573c0; 576 result.m_blocks[21] = 0x65f9ef17; 577 result.m_blocks[22] = 0x55bc28f2; 578 result.m_blocks[23] = 0x80dcc7f7; 579 result.m_blocks[24] = 0xf46eeddc; 580 result.m_blocks[25] = 0x5fdcefce; 581 result.m_blocks[26] = 0x000553f7; 582 } 583 584 return result; 585 } 586 587 //****************************************************************************** 588 // result = 10^exponent 589 //****************************************************************************** 590 private static void BigInt_Pow10(out tBigInt pResult, uint exponent) 591 { 592 // make sure the exponent is within the bounds of the lookup table data 593 // RJ_ASSERT(exponent < 512); 594 595 // create two temporary values to reduce large integer copy operations 596 tBigInt temp1 = default; 597 tBigInt temp2 = default; 598 ref tBigInt pCurTemp = ref temp1; 599 ref tBigInt pNextTemp = ref temp2; 600 601 // initialize the result by looking up a 32-bit power of 10 corresponding to the first 3 bits 602 uint smallExponent = exponent & 0x7; 603 pCurTemp.SetU32(g_PowerOf10_U32[smallExponent]); 604 605 // remove the low bits that we used for the 32-bit lookup table 606 exponent >>= 3; 607 int tableIdx = 0; 608 // while there are remaining bits in the exponent to be processed 609 while (exponent != 0) 610 { 611 // if the current bit is set, multiply it with the corresponding power of 10 612 if ((exponent & 1) != 0) 613 { 614 // multiply into the next temporary 615 BigInt_Multiply(out pNextTemp, pCurTemp, g_PowerOf10_Big(tableIdx)); 616 617 // swap to the next temporary 618 ref tBigInt pSwap = ref pCurTemp; 619 pCurTemp = pNextTemp; 620 pNextTemp = pSwap; 621 } 622 623 // advance to the next bit 624 ++tableIdx; 625 exponent >>= 1; 626 } 627 628 // output the result 629 pResult = pCurTemp; 630 } 631 632 633 //****************************************************************************** 634 // result = in * 10^exponent 635 //****************************************************************************** 636 private static unsafe void BigInt_MultiplyPow10(out tBigInt pResult, in tBigInt input, uint exponent) 637 { 638 // make sure the exponent is within the bounds of the lookup table data 639 // RJ_ASSERT(exponent < 512); 640 641 // create two temporary values to reduce large integer copy operations 642 tBigInt temp1 = default; 643 tBigInt temp2 = default; 644 ref tBigInt pCurTemp = ref temp1; 645 ref tBigInt pNextTemp = ref temp2; 646 647 // initialize the result by looking up a 32-bit power of 10 corresponding to the first 3 bits 648 uint smallExponent = exponent & 0x7; 649 if (smallExponent != 0) 650 { 651 BigInt_Multiply(out pCurTemp, input, g_PowerOf10_U32[smallExponent]); 652 } 653 else 654 { 655 pCurTemp = input; 656 } 657 658 // remove the low bits that we used for the 32-bit lookup table 659 exponent >>= 3; 660 int tableIdx = 0; 661 662 // while there are remaining bits in the exponent to be processed 663 while (exponent != 0) 664 { 665 // if the current bit is set, multiply it with the corresponding power of 10 666 if((exponent & 1) != 0) 667 { 668 // multiply into the next temporary 669 BigInt_Multiply( out pNextTemp, pCurTemp, g_PowerOf10_Big(tableIdx) ); 670 671 // swap to the next temporary 672 ref tBigInt pSwap = ref pCurTemp; 673 pCurTemp = pNextTemp; 674 pNextTemp = pSwap; 675 } 676 677 // advance to the next bit 678 ++tableIdx; 679 exponent >>= 1; 680 } 681 682 // output the result 683 pResult = pCurTemp; 684 } 685 686 //****************************************************************************** 687 // result = 2^exponent 688 //****************************************************************************** 689 private static unsafe void BigInt_Pow2(out tBigInt pResult, uint exponent) 690 { 691 int blockIdx = (int)exponent / 32; 692 //RJ_ASSERT(blockIdx < c_BigInt_MaxBlocks); 693 694 for (uint i = 0; i <= blockIdx; ++i) 695 pResult.m_blocks[i] = 0; 696 697 pResult.m_length = blockIdx + 1; 698 699 int bitIdx = ((int)exponent % 32); 700 pResult.m_blocks[blockIdx] |= (uint)(1 << bitIdx); 701 } 702 703 //****************************************************************************** 704 // This function will divide two large numbers under the assumption that the 705 // result is within the range [0,10) and the input numbers have been shifted 706 // to satisfy: 707 // - The highest block of the divisor is greater than or equal to 8 such that 708 // there is enough precision to make an accurate first guess at the quotient. 709 // - The highest block of the divisor is less than the maximum value on an 710 // unsigned 32-bit integer such that we can safely increment without overflow. 711 // - The dividend does not contain more blocks than the divisor such that we 712 // can estimate the quotient by dividing the equivalently placed high blocks. 713 // 714 // quotient = floor(dividend / divisor) 715 // remainder = dividend - quotient*divisor 716 // 717 // pDividend is updated to be the remainder and the quotient is returned. 718 //****************************************************************************** 719 private static unsafe uint BigInt_DivideWithRemainder_MaxQuotient9(ref tBigInt pDividend, in tBigInt divisor) 720 { 721 // Check that the divisor has been correctly shifted into range and that it is not 722 // smaller than the dividend in length. 723 //RJ_ASSERT( !divisor.IsZero() && 724 // divisor.m_blocks[divisor.m_length-1] >= 8 && 725 // divisor.m_blocks[divisor.m_length-1] < 0xFFFFFFFF && 726 // pDividend->m_length <= divisor.m_length ); 727 728 // If the dividend is smaller than the divisor, the quotient is zero and the divisor is already 729 // the remainder. 730 int length = divisor.m_length; 731 if (pDividend.m_length < divisor.m_length) 732 return 0; 733 734 fixed (uint* pDivisorCur1 = divisor.m_blocks) 735 fixed (uint* pDividendCur1 = pDividend.m_blocks) 736 { 737 uint* pDivisorCur = pDivisorCur1; 738 uint* pDividendCur = pDividendCur1; 739 740 uint* pFinalDivisorBlock = pDivisorCur + length - 1; 741 uint* pFinalDividendBlock = pDividendCur + length - 1; 742 743 // Compute an estimated quotient based on the high block value. This will either match the actual quotient or 744 // undershoot by one. 745 uint quotient = *pFinalDividendBlock / (*pFinalDivisorBlock + 1); 746 //RJ_ASSERT(quotient <= 9); 747 748 // Divide out the estimated quotient 749 if (quotient != 0) 750 { 751 // dividend = dividend - divisor*quotient 752 ulong borrow = 0; 753 ulong carry = 0; 754 do 755 { 756 ulong product = (ulong) *pDivisorCur * (ulong) quotient + carry; 757 carry = product >> 32; 758 759 ulong difference = (ulong) *pDividendCur - (product & 0xFFFFFFFF) - borrow; 760 borrow = (difference >> 32) & 1; 761 762 *pDividendCur = (uint) (difference & 0xFFFFFFFF); 763 764 ++pDivisorCur; 765 ++pDividendCur; 766 } while (pDivisorCur <= pFinalDivisorBlock); 767 768 // remove all leading zero blocks from dividend 769 while (length > 0 && pDividend.m_blocks[length - 1] == 0) 770 --length; 771 772 pDividend.m_length = length; 773 } 774 775 // If the dividend is still larger than the divisor, we overshot our estimate quotient. To correct, 776 // we increment the quotient and subtract one more divisor from the dividend. 777 if (BigInt_Compare(pDividend, divisor) >= 0) 778 { 779 ++quotient; 780 781 // dividend = dividend - divisor 782 pDivisorCur = pDivisorCur1; 783 pDividendCur = pDividendCur1; 784 785 ulong borrow = 0; 786 do 787 { 788 ulong difference = (ulong) *pDividendCur - (ulong) *pDivisorCur - borrow; 789 borrow = (difference >> 32) & 1; 790 791 *pDividendCur = (uint)(difference & 0xFFFFFFFF); 792 793 ++pDivisorCur; 794 ++pDividendCur; 795 } while (pDivisorCur <= pFinalDivisorBlock); 796 797 // remove all leading zero blocks from dividend 798 while (length > 0 && pDividend.m_blocks[length - 1] == 0) 799 --length; 800 801 pDividend.m_length = length; 802 } 803 804 return quotient; 805 } 806 } 807 808 809 //****************************************************************************** 810 // result = result << shift 811 //****************************************************************************** 812 private static unsafe void BigInt_ShiftLeft(ref tBigInt pResult, uint shift) 813 { 814 // RJ_ASSERT( shift != 0 ); 815 816 int shiftBlocks = (int)shift / 32; 817 int shiftBits = (int)shift % 32; 818 819 int inLength = pResult.m_length; 820 // RJ_ASSERT( inLength + shiftBlocks <= c_BigInt_MaxBlocks ); 821 822 // check if the shift is block aligned 823 if (shiftBits == 0) 824 { 825 // process blocks high to low so that we can safely process in place 826 fixed (uint* pInBlocks1 = pResult.m_blocks) 827 { 828 uint* pInBlocks = pInBlocks1; 829 uint* pInCur = pInBlocks + inLength - 1; 830 uint* pOutCur = pInCur + shiftBlocks; 831 832 // copy blocks from high to low 833 for (; pInCur >= pInBlocks; --pInCur, --pOutCur) 834 { 835 *pOutCur = *pInCur; 836 } 837 } 838 839 // zero the remaining low blocks 840 for ( uint i = 0; i < shiftBlocks; ++i) 841 pResult.m_blocks[i] = 0; 842 843 pResult.m_length += shiftBlocks; 844 } 845 // else we need to shift partial blocks 846 else 847 { 848 int inBlockIdx = inLength - 1; 849 int outBlockIdx = inLength + shiftBlocks; 850 851 // set the length to hold the shifted blocks 852 //RJ_ASSERT( outBlockIdx < c_BigInt_MaxBlocks ); 853 pResult.m_length = outBlockIdx + 1; 854 855 // output the initial blocks 856 int lowBitsShift = (32 - shiftBits); 857 uint highBits = 0; 858 uint block = pResult.m_blocks[inBlockIdx]; 859 uint lowBits = block >> lowBitsShift; 860 while ( inBlockIdx > 0 ) 861 { 862 pResult.m_blocks[outBlockIdx] = highBits | lowBits; 863 highBits = block << shiftBits; 864 865 --inBlockIdx; 866 --outBlockIdx; 867 868 block = pResult.m_blocks[inBlockIdx]; 869 lowBits = block >> lowBitsShift; 870 } 871 872 // output the final blocks 873 // RJ_ASSERT( outBlockIdx == shiftBlocks + 1 ); 874 pResult.m_blocks[outBlockIdx] = highBits | lowBits; 875 pResult.m_blocks[outBlockIdx-1] = block << shiftBits; 876 877 // zero the remaining low blocks 878 for ( uint i = 0; i < shiftBlocks; ++i) 879 pResult.m_blocks[i] = 0; 880 881 // check if the terminating block has no set bits 882 if (pResult.m_blocks[pResult.m_length - 1] == 0) 883 --pResult.m_length; 884 } 885 } 886 887 //****************************************************************************** 888 // Different modes for terminating digit output 889 //****************************************************************************** 890 public enum CutoffMode 891 { 892 Unique, // as many digits as necessary to print a uniquely identifiable number 893 TotalLength, // up to cutoffNumber significant digits 894 FractionLength, // up to cutoffNumber significant digits past the decimal point 895 }; 896 897 //****************************************************************************** 898 // This is an implementation the Dragon4 algorithm to convert a binary number 899 // in floating point format to a decimal number in string format. The function 900 // returns the number of digits written to the output buffer and the output is 901 // not NUL terminated. 902 // 903 // The floating point input value is (mantissa * 2^exponent). 904 // 905 // See the following papers for more information on the algorithm: 906 // "How to Print Floating-Point Numbers Accurately" 907 // Steele and White 908 // http://kurtstephens.com/files/p372-steele.pdf 909 // "Printing Floating-Point Numbers Quickly and Accurately" 910 // Burger and Dybvig 911 // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.4656&rep=rep1&type=pdf 912 //****************************************************************************** 913 private static unsafe uint Dragon4 914 ( 915 ulong mantissa, // value significand 916 int exponent, // value exponent in base 2 917 uint mantissaHighBitIdx, // index of the highest set mantissa bit 918 bool hasUnequalMargins, // is the high margin twice as large as the low margin 919 CutoffMode cutoffMode, // how to determine output length 920 uint cutoffNumber, // parameter to the selected cutoffMode 921 byte* pOutBuffer, // buffer to output into 922 uint bufferSize, // maximum characters that can be printed to pOutBuffer 923 out int pOutExponent // the base 10 exponent of the first digit 924 ) 925 { 926 byte* pCurDigit = pOutBuffer; 927 928 // RJ_ASSERT( bufferSize > 0 ); 929 930 // if the mantissa is zero, the value is zero regardless of the exponent 931 if (mantissa == 0) 932 { 933 *pCurDigit = (byte)'0'; 934 pOutExponent = 0; 935 return 1; 936 } 937 938 // compute the initial state in integral form such that 939 // value = scaledValue / scale 940 // marginLow = scaledMarginLow / scale 941 tBigInt scale = default; // positive scale applied to value and margin such that they can be 942 // represented as whole numbers 943 tBigInt scaledValue = default; // scale * mantissa 944 tBigInt scaledMarginLow = default; // scale * 0.5 * (distance between this floating-point number and its 945 // immediate lower value) 946 947 // For normalized IEEE floating point values, each time the exponent is incremented the margin also 948 // doubles. That creates a subset of transition numbers where the high margin is twice the size of 949 // the low margin. 950 tBigInt * pScaledMarginHigh; 951 tBigInt optionalMarginHigh = default; 952 953 if ( hasUnequalMargins ) 954 { 955 // if we have no fractional component 956 if (exponent > 0) 957 { 958 // 1) Expand the input value by multiplying out the mantissa and exponent. This represents 959 // the input value in its whole number representation. 960 // 2) Apply an additional scale of 2 such that later comparisons against the margin values 961 // are simplified. 962 // 3) Set the margin value to the lowest mantissa bit's scale. 963 964 // scaledValue = 2 * 2 * mantissa*2^exponent 965 scaledValue.SetU64( 4 * mantissa ); 966 BigInt_ShiftLeft(ref scaledValue, (uint)exponent); 967 968 // scale = 2 * 2 * 1 969 scale.SetU32( 4 ); 970 971 // scaledMarginLow = 2 * 2^(exponent-1) 972 BigInt_Pow2( out scaledMarginLow, (uint)exponent ); 973 974 // scaledMarginHigh = 2 * 2 * 2^(exponent-1) 975 BigInt_Pow2( out optionalMarginHigh, (uint)(exponent + 1)); 976 } 977 // else we have a fractional exponent 978 else 979 { 980 // In order to track the mantissa data as an integer, we store it as is with a large scale 981 982 // scaledValue = 2 * 2 * mantissa 983 scaledValue.SetU64( 4 * mantissa ); 984 985 // scale = 2 * 2 * 2^(-exponent) 986 BigInt_Pow2(out scale, (uint)(-exponent + 2)); 987 988 // scaledMarginLow = 2 * 2^(-1) 989 scaledMarginLow.SetU32( 1 ); 990 991 // scaledMarginHigh = 2 * 2 * 2^(-1) 992 optionalMarginHigh.SetU32( 2 ); 993 } 994 995 // the high and low margins are different 996 pScaledMarginHigh = &optionalMarginHigh; 997 } 998 else 999 { 1000 // if we have no fractional component 1001 if (exponent > 0) 1002 { 1003 // 1) Expand the input value by multiplying out the mantissa and exponent. This represents 1004 // the input value in its whole number representation. 1005 // 2) Apply an additional scale of 2 such that later comparisons against the margin values 1006 // are simplified. 1007 // 3) Set the margin value to the lowest mantissa bit's scale. 1008 1009 // scaledValue = 2 * mantissa*2^exponent 1010 scaledValue.SetU64( 2 * mantissa ); 1011 BigInt_ShiftLeft(ref scaledValue, (uint)exponent); 1012 1013 // scale = 2 * 1 1014 scale.SetU32( 2 ); 1015 1016 // scaledMarginLow = 2 * 2^(exponent-1) 1017 BigInt_Pow2(out scaledMarginLow, (uint)exponent ); 1018 } 1019 // else we have a fractional exponent 1020 else 1021 { 1022 // In order to track the mantissa data as an integer, we store it as is with a large scale 1023 1024 // scaledValue = 2 * mantissa 1025 scaledValue.SetU64( 2 * mantissa ); 1026 1027 // scale = 2 * 2^(-exponent) 1028 BigInt_Pow2(out scale, (uint)(-exponent + 1)); 1029 1030 // scaledMarginLow = 2 * 2^(-1) 1031 scaledMarginLow.SetU32( 1 ); 1032 } 1033 1034 // the high and low margins are equal 1035 pScaledMarginHigh = &scaledMarginLow; 1036 } 1037 1038 // Compute an estimate for digitExponent that will be correct or undershoot by one. 1039 // This optimization is based on the paper "Printing Floating-Point Numbers Quickly and Accurately" 1040 // by Burger and Dybvig http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.4656&rep=rep1&type=pdf 1041 // We perform an additional subtraction of 0.69 to increase the frequency of a failed estimate 1042 // because that lets us take a faster branch in the code. 0.69 is chosen because 0.69 + log10(2) is 1043 // less than one by a reasonable epsilon that will account for any floating point error. 1044 // 1045 // We want to set digitExponent to floor(log10(v)) + 1 1046 // v = mantissa*2^exponent 1047 // log2(v) = log2(mantissa) + exponent; 1048 // log10(v) = log2(v) * log10(2) 1049 // floor(log2(v)) = mantissaHighBitIdx + exponent; 1050 // log10(v) - log10(2) < (mantissaHighBitIdx + exponent) * log10(2) <= log10(v) 1051 // log10(v) < (mantissaHighBitIdx + exponent) * log10(2) + log10(2) <= log10(v) + log10(2) 1052 // floor( log10(v) ) < ceil( (mantissaHighBitIdx + exponent) * log10(2) ) <= floor( log10(v) ) + 1 1053 const double log10_2 = 0.30102999566398119521373889472449; 1054 var digitExponentDoubleValue = (double) ((int) mantissaHighBitIdx + exponent) * log10_2 - 0.69; 1055 digitExponentDoubleValue = Math.Ceiling(digitExponentDoubleValue); 1056 int digitExponent = (int)digitExponentDoubleValue; 1057 1058 // if the digit exponent is smaller than the smallest desired digit for fractional cutoff, 1059 // pull the digit back into legal range at which point we will round to the appropriate value. 1060 // Note that while our value for digitExponent is still an estimate, this is safe because it 1061 // only increases the number. This will either correct digitExponent to an accurate value or it 1062 // will clamp it above the accurate value. 1063 if (cutoffMode == CutoffMode.FractionLength && digitExponent <= -(int)cutoffNumber) 1064 { 1065 digitExponent = -(int)cutoffNumber + 1; 1066 } 1067 1068 // Divide value by 10^digitExponent. 1069 if (digitExponent > 0) 1070 { 1071 // The exponent is positive creating a division so we multiply up the scale. 1072 tBigInt temp; 1073 BigInt_MultiplyPow10( out temp, scale, (uint)digitExponent ); 1074 scale = temp; 1075 } 1076 else if (digitExponent < 0) 1077 { 1078 // The exponent is negative creating a multiplication so we multiply up the scaledValue, 1079 // scaledMarginLow and scaledMarginHigh. 1080 tBigInt pow10; 1081 BigInt_Pow10(out pow10, (uint)(-digitExponent)); 1082 1083 tBigInt temp; 1084 BigInt_Multiply( out temp, scaledValue, pow10); 1085 scaledValue = temp; 1086 1087 BigInt_Multiply( out temp, scaledMarginLow, pow10); 1088 scaledMarginLow = temp; 1089 1090 if (pScaledMarginHigh != &scaledMarginLow) 1091 BigInt_Multiply2( out *pScaledMarginHigh, scaledMarginLow ); 1092 } 1093 1094 // If (value >= 1), our estimate for digitExponent was too low 1095 if( BigInt_Compare(scaledValue,scale) >= 0 ) 1096 { 1097 // The exponent estimate was incorrect. 1098 // Increment the exponent and don't perform the premultiply needed 1099 // for the first loop iteration. 1100 digitExponent = digitExponent + 1; 1101 } 1102 else 1103 { 1104 // The exponent estimate was correct. 1105 // Multiply larger by the output base to prepare for the first loop iteration. 1106 BigInt_Multiply10( ref scaledValue ); 1107 BigInt_Multiply10( ref scaledMarginLow ); 1108 if (pScaledMarginHigh != &scaledMarginLow) 1109 BigInt_Multiply2( out *pScaledMarginHigh, scaledMarginLow ); 1110 } 1111 1112 // Compute the cutoff exponent (the exponent of the final digit to print). 1113 // Default to the maximum size of the output buffer. 1114 int cutoffExponent = digitExponent - (int)bufferSize; 1115 switch (cutoffMode) 1116 { 1117 // print digits until we pass the accuracy margin limits or buffer size 1118 case CutoffMode.Unique: 1119 break; 1120 1121 // print cutoffNumber of digits or until we reach the buffer size 1122 case CutoffMode.TotalLength: 1123 { 1124 int desiredCutoffExponent = digitExponent - (int) cutoffNumber; 1125 if (desiredCutoffExponent > cutoffExponent) 1126 cutoffExponent = desiredCutoffExponent; 1127 } 1128 break; 1129 1130 // print cutoffNumber digits past the decimal point or until we reach the buffer size 1131 case CutoffMode.FractionLength: 1132 { 1133 int desiredCutoffExponent = -(int) cutoffNumber; 1134 if (desiredCutoffExponent > cutoffExponent) 1135 cutoffExponent = desiredCutoffExponent; 1136 } 1137 break; 1138 } 1139 1140 // Output the exponent of the first digit we will print 1141 pOutExponent = digitExponent-1; 1142 1143 // In preparation for calling BigInt_DivideWithRemainder_MaxQuotient9(), 1144 // we need to scale up our values such that the highest block of the denominator 1145 // is greater than or equal to 8. We also need to guarantee that the numerator 1146 // can never have a length greater than the denominator after each loop iteration. 1147 // This requires the highest block of the denominator to be less than or equal to 1148 // 429496729 which is the highest number that can be multiplied by 10 without 1149 // overflowing to a new block. 1150 // RJ_ASSERT( scale.GetLength() > 0 ); 1151 uint hiBlock = scale.GetBlock( scale.GetLength() - 1 ); 1152 if (hiBlock < 8 || hiBlock > 429496729) 1153 { 1154 // Perform a bit shift on all values to get the highest block of the denominator into 1155 // the range [8,429496729]. We are more likely to make accurate quotient estimations 1156 // in BigInt_DivideWithRemainder_MaxQuotient9() with higher denominator values so 1157 // we shift the denominator to place the highest bit at index 27 of the highest block. 1158 // This is safe because (2^28 - 1) = 268435455 which is less than 429496729. This means 1159 // that all values with a highest bit at index 27 are within range. 1160 uint hiBlockLog2 = LogBase2(hiBlock); 1161 // RJ_ASSERT(hiBlockLog2 < 3 || hiBlockLog2 > 27); 1162 uint shift = (32 + 27 - hiBlockLog2) % 32; 1163 1164 BigInt_ShiftLeft( ref scale, shift ); 1165 BigInt_ShiftLeft( ref scaledValue, shift); 1166 BigInt_ShiftLeft( ref scaledMarginLow, shift); 1167 if (pScaledMarginHigh != &scaledMarginLow) 1168 BigInt_Multiply2( out *pScaledMarginHigh, scaledMarginLow ); 1169 } 1170 1171 // These values are used to inspect why the print loop terminated so we can properly 1172 // round the final digit. 1173 bool low; // did the value get within marginLow distance from zero 1174 bool high; // did the value get within marginHigh distance from one 1175 uint outputDigit; // current digit being output 1176 1177 if (cutoffMode == CutoffMode.Unique) 1178 { 1179 // For the unique cutoff mode, we will try to print until we have reached a level of 1180 // precision that uniquely distinguishes this value from its neighbors. If we run 1181 // out of space in the output buffer, we terminate early. 1182 for (;;) 1183 { 1184 digitExponent = digitExponent-1; 1185 1186 // divide out the scale to extract the digit 1187 outputDigit = BigInt_DivideWithRemainder_MaxQuotient9(ref scaledValue, scale); 1188 //RJ_ASSERT( outputDigit < 10 ); 1189 1190 // update the high end of the value 1191 tBigInt scaledValueHigh; 1192 BigInt_Add( out scaledValueHigh, scaledValue, *pScaledMarginHigh ); 1193 1194 // stop looping if we are far enough away from our neighboring values 1195 // or if we have reached the cutoff digit 1196 low = BigInt_Compare(scaledValue, scaledMarginLow) < 0; 1197 high = BigInt_Compare(scaledValueHigh, scale) > 0; 1198 if (low | high | (digitExponent == cutoffExponent)) 1199 break; 1200 1201 // store the output digit 1202 *pCurDigit = (byte)('0' + outputDigit); 1203 ++pCurDigit; 1204 1205 // multiply larger by the output base 1206 BigInt_Multiply10( ref scaledValue ); 1207 BigInt_Multiply10( ref scaledMarginLow ); 1208 if (pScaledMarginHigh != &scaledMarginLow) 1209 BigInt_Multiply2( out *pScaledMarginHigh, scaledMarginLow ); 1210 } 1211 } 1212 else 1213 { 1214 // For length based cutoff modes, we will try to print until we 1215 // have exhausted all precision (i.e. all remaining digits are zeros) or 1216 // until we reach the desired cutoff digit. 1217 low = false; 1218 high = false; 1219 1220 for (;;) 1221 { 1222 digitExponent = digitExponent-1; 1223 1224 // divide out the scale to extract the digit 1225 outputDigit = BigInt_DivideWithRemainder_MaxQuotient9(ref scaledValue, scale); 1226 //RJ_ASSERT( outputDigit < 10 ); 1227 1228 if ( scaledValue.IsZero() | (digitExponent == cutoffExponent) ) 1229 break; 1230 1231 // store the output digit 1232 *pCurDigit = (byte)('0' + outputDigit); 1233 ++pCurDigit; 1234 1235 // multiply larger by the output base 1236 BigInt_Multiply10(ref scaledValue); 1237 } 1238 } 1239 1240 // round off the final digit 1241 // default to rounding down if value got too close to 0 1242 bool roundDown = low; 1243 1244 // if it is legal to round up and down 1245 if (low == high) 1246 { 1247 // round to the closest digit by comparing value with 0.5. To do this we need to convert 1248 // the inequality to large integer values. 1249 // compare( value, 0.5 ) 1250 // compare( scale * value, scale * 0.5 ) 1251 // compare( 2 * scale * value, scale ) 1252 BigInt_Multiply2(ref scaledValue); 1253 int compare = BigInt_Compare(scaledValue, scale); 1254 roundDown = compare < 0; 1255 1256 // if we are directly in the middle, round towards the even digit (i.e. IEEE rouding rules) 1257 if (compare == 0) 1258 roundDown = (outputDigit & 1) == 0; 1259 } 1260 1261 // print the rounded digit 1262 if (roundDown) 1263 { 1264 *pCurDigit = (byte)('0' + outputDigit); 1265 ++pCurDigit; 1266 } 1267 else 1268 { 1269 // handle rounding up 1270 if (outputDigit == 9) 1271 { 1272 // find the first non-nine prior digit 1273 for (;;) 1274 { 1275 // if we are at the first digit 1276 if (pCurDigit == pOutBuffer) 1277 { 1278 // output 1 at the next highest exponent 1279 *pCurDigit = (byte)'1'; 1280 ++pCurDigit; 1281 pOutExponent += 1; 1282 break; 1283 } 1284 1285 --pCurDigit; 1286 if (*pCurDigit != (byte)'9') 1287 { 1288 // increment the digit 1289 *pCurDigit += 1; 1290 ++pCurDigit; 1291 break; 1292 } 1293 } 1294 } 1295 else 1296 { 1297 // values in the range [0,8] can perform a simple round up 1298 *pCurDigit = (byte)((byte)'0' + outputDigit + 1); 1299 ++pCurDigit; 1300 } 1301 } 1302 1303 // return the number of digits output 1304 uint outputLen = (uint)(pCurDigit - pOutBuffer); 1305 // RJ_ASSERT(outputLen <= bufferSize); 1306 return outputLen; 1307 } 1308 1309 //****************************************************************************** 1310 //****************************************************************************** 1311 public enum PrintFloatFormat 1312 { 1313 Positional, // [-]ddddd.dddd 1314 Scientific, // [-]d.dddde[sign]ddd 1315 } 1316 1317 //******************************************************************************\ 1318 // Helper union to decompose a 32-bit IEEE float. 1319 // sign: 1 bit 1320 // exponent: 8 bits 1321 // mantissa: 23 bits 1322 //****************************************************************************** 1323 [StructLayout(LayoutKind.Explicit)] 1324 public struct tFloatUnion32 1325 { 1326 public bool IsNegative() { return (m_integer >> 31) != 0; } 1327 public uint GetExponent() { return (m_integer >> 23) & 0xFF; } 1328 public uint GetMantissa() { return m_integer & 0x7FFFFF; } 1329 1330 [FieldOffset(0)] 1331 public float m_floatingPoint; 1332 1333 [FieldOffset(0)] 1334 public uint m_integer; 1335 }; 1336 1337 //****************************************************************************** 1338 // Helper union to decompose a 64-bit IEEE float. 1339 // sign: 1 bit 1340 // exponent: 11 bits 1341 // mantissa: 52 bits 1342 //****************************************************************************** 1343 [StructLayout(LayoutKind.Explicit)] 1344 public struct tFloatUnion64 1345 { 1346 public bool IsNegative() { return (m_integer >> 63) != 0; } 1347 public uint GetExponent() { return (uint)((m_integer >> 52) & 0x7FF); } 1348 public ulong GetMantissa() { return m_integer & 0xFFFFFFFFFFFFFUL; } 1349 1350 [FieldOffset(0)] 1351 public double m_floatingPoint; 1352 1353 [FieldOffset(0)] 1354 public ulong m_integer; 1355 }; 1356 1357 1358 //****************************************************************************** 1359 // Outputs the positive number with positional notation: ddddd.dddd 1360 // The output is always NUL terminated and the output length (not including the 1361 // NUL) is returned. 1362 //****************************************************************************** 1363 private static unsafe int FormatPositional 1364 ( 1365 byte* pOutBuffer, // buffer to output into 1366 uint bufferSize, // maximum characters that can be printed to pOutBuffer 1367 ulong mantissa, // value significand 1368 int exponent, // value exponent in base 2 1369 uint mantissaHighBitIdx, // index of the highest set mantissa bit 1370 bool hasUnequalMargins, // is the high margin twice as large as the low margin 1371 int precision // Negative prints as many digits as are needed for a unique 1372 // number. Positive specifies the maximum number of 1373 // significant digits to print past the decimal point. 1374 ) 1375 { 1376 //RJ_ASSERT(bufferSize > 0); 1377 1378 int printExponent; 1379 uint numPrintDigits; 1380 1381 uint maxPrintLen = bufferSize - 1; 1382 1383 if (precision < 0) 1384 { 1385 numPrintDigits = Dragon4(mantissa, 1386 exponent, 1387 mantissaHighBitIdx, 1388 hasUnequalMargins, 1389 CutoffMode.Unique, 1390 0, 1391 pOutBuffer, 1392 maxPrintLen, 1393 out printExponent); 1394 } 1395 else 1396 { 1397 numPrintDigits = Dragon4(mantissa, 1398 exponent, 1399 mantissaHighBitIdx, 1400 hasUnequalMargins, 1401 CutoffMode.FractionLength, 1402 (uint)precision, 1403 pOutBuffer, 1404 maxPrintLen, 1405 out printExponent); 1406 } 1407 1408 //RJ_ASSERT(numPrintDigits > 0); 1409 //RJ_ASSERT(numPrintDigits <= bufferSize); 1410 1411 // track the number of digits past the decimal point that have been printed 1412 uint numFractionDigits = 0; 1413 1414 // if output has a whole number 1415 if (printExponent >= 0) 1416 { 1417 // leave the whole number at the start of the buffer 1418 uint numWholeDigits = (uint)(printExponent + 1); 1419 if (numPrintDigits < numWholeDigits) 1420 { 1421 // don't overflow the buffer 1422 if (numWholeDigits > maxPrintLen) 1423 numWholeDigits = maxPrintLen; 1424 1425 // add trailing zeros up to the decimal point 1426 for (; numPrintDigits < numWholeDigits; ++numPrintDigits) 1427 pOutBuffer[numPrintDigits] = (byte)'0'; 1428 } 1429 // insert the decimal point prior to the fraction 1430 else if (numPrintDigits > (uint)numWholeDigits) 1431 { 1432 numFractionDigits = numPrintDigits - numWholeDigits; 1433 uint maxFractionDigits = maxPrintLen - numWholeDigits - 1; 1434 if (numFractionDigits > maxFractionDigits) 1435 numFractionDigits = maxFractionDigits; 1436 Unsafe.CopyBlock(pOutBuffer + numWholeDigits + 1, pOutBuffer + numWholeDigits, numFractionDigits); 1437 pOutBuffer[numWholeDigits] = (byte)'.'; 1438 numPrintDigits = numWholeDigits + 1 + numFractionDigits; 1439 } 1440 } 1441 else 1442 { 1443 // shift out the fraction to make room for the leading zeros 1444 if (maxPrintLen > 2) 1445 { 1446 uint numFractionZeros = (uint)( - printExponent - 1); 1447 uint maxFractionZeros = maxPrintLen - 2; 1448 if (numFractionZeros > maxFractionZeros) 1449 numFractionZeros = maxFractionZeros; 1450 1451 uint digitsStartIdx = 2 + numFractionZeros; 1452 1453 // shift the significant digits right such that there is room for leading zeros 1454 numFractionDigits = numPrintDigits; 1455 uint maxFractionDigits = maxPrintLen - digitsStartIdx; 1456 if (numFractionDigits > maxFractionDigits) 1457 numFractionDigits = maxFractionDigits; 1458 Unsafe.CopyBlock(pOutBuffer + digitsStartIdx, pOutBuffer, numFractionDigits); 1459 1460 // insert the leading zeros 1461 for (uint i = 2; i < digitsStartIdx; ++i) 1462 pOutBuffer[i] = (byte)'0'; 1463 1464 // update the counts 1465 numFractionDigits += numFractionZeros; 1466 numPrintDigits = numFractionDigits; 1467 } 1468 1469 // add the decimal point 1470 if (maxPrintLen > 1) 1471 { 1472 pOutBuffer[1] = (byte)'.'; 1473 numPrintDigits += 1; 1474 } 1475 1476 // add the initial zero 1477 if (maxPrintLen > 0) 1478 { 1479 pOutBuffer[0] = (byte)'0'; 1480 numPrintDigits += 1; 1481 } 1482 } 1483 1484 // add trailing zeros up to precision length 1485 if (precision > (int)numFractionDigits && numPrintDigits < maxPrintLen) 1486 { 1487 // add a decimal point if this is the first fractional digit we are printing 1488 if (numFractionDigits == 0) 1489 { 1490 pOutBuffer[numPrintDigits++] = (byte)'.'; 1491 } 1492 1493 // compute the number of trailing zeros needed 1494 uint totalDigits = (uint)(numPrintDigits + (precision - (int)numFractionDigits)); 1495 if (totalDigits > maxPrintLen) 1496 totalDigits = maxPrintLen; 1497 1498 for (; numPrintDigits < totalDigits; ++numPrintDigits) 1499 pOutBuffer[numPrintDigits] = (byte)'0'; 1500 } 1501 1502 // terminate the buffer 1503 //RJ_ASSERT(numPrintDigits <= maxPrintLen); 1504 //pOutBuffer[numPrintDigits] = '\0'; 1505 1506 return (int)numPrintDigits; 1507 } 1508 1509 1510 1511 //****************************************************************************** 1512 // Outputs the positive number with scientific notation: d.dddde[sign]ddd 1513 // The output is always NUL terminated and the output length (not including the 1514 // NUL) is returned. 1515 //****************************************************************************** 1516 private static unsafe int FormatScientific 1517 ( 1518 byte* pOutBuffer, // buffer to output into 1519 uint bufferSize, // maximum characters that can be printed to pOutBuffer 1520 ulong mantissa, // value significand 1521 int exponent, // value exponent in base 2 1522 uint mantissaHighBitIdx, // index of the highest set mantissa bit 1523 bool hasUnequalMargins, // is the high margin twice as large as the low margin 1524 int precision // Negative prints as many digits as are needed for a unique 1525 // number. Positive specifies the maximum number of 1526 // significant digits to print past the decimal point. 1527 ) 1528 { 1529 //RJ_ASSERT(bufferSize > 0); 1530 1531 int printExponent; 1532 uint numPrintDigits; 1533 1534 if (precision < 0) 1535 { 1536 numPrintDigits = Dragon4(mantissa, 1537 exponent, 1538 mantissaHighBitIdx, 1539 hasUnequalMargins, 1540 CutoffMode.Unique, 1541 0, 1542 pOutBuffer, 1543 bufferSize, 1544 out printExponent); 1545 } 1546 else 1547 { 1548 numPrintDigits = Dragon4(mantissa, 1549 exponent, 1550 mantissaHighBitIdx, 1551 hasUnequalMargins, 1552 CutoffMode.TotalLength, 1553 (uint)(precision + 1), 1554 pOutBuffer, 1555 bufferSize, 1556 out printExponent); 1557 } 1558 1559 //RJ_ASSERT(numPrintDigits > 0); 1560 //RJ_ASSERT(numPrintDigits <= bufferSize); 1561 1562 byte* pCurOut = pOutBuffer; 1563 1564 // keep the whole number as the first digit 1565 if (bufferSize > 1) 1566 { 1567 pCurOut += 1; 1568 bufferSize -= 1; 1569 } 1570 1571 // insert the decimal point prior to the fractional number 1572 uint numFractionDigits = numPrintDigits - 1; 1573 if (numFractionDigits > 0 && bufferSize > 1) 1574 { 1575 uint maxFractionDigits = bufferSize - 2; 1576 if (numFractionDigits > maxFractionDigits) 1577 numFractionDigits = maxFractionDigits; 1578 Unsafe.CopyBlock(pCurOut + 1, pCurOut, numFractionDigits); 1579 pCurOut[0] = (byte)'.'; 1580 pCurOut += (1 + numFractionDigits); 1581 bufferSize -= (1 + numFractionDigits); 1582 } 1583 1584 // add trailing zeros up to precision length 1585 if (precision > (int)numFractionDigits && bufferSize > 1) 1586 { 1587 // add a decimal point if this is the first fractional digit we are printing 1588 if (numFractionDigits == 0) 1589 { 1590 *pCurOut = (byte)'.'; 1591 ++pCurOut; 1592 --bufferSize; 1593 } 1594 1595 // compute the number of trailing zeros needed 1596 uint numZeros = (uint)(precision - numFractionDigits); 1597 if (numZeros > bufferSize - 1) 1598 numZeros = bufferSize - 1; 1599 1600 for (byte* pEnd = pCurOut + numZeros; pCurOut < pEnd; ++pCurOut) 1601 *pCurOut = (byte)'0'; 1602 } 1603 1604 // print the exponent into a local buffer and copy into output buffer 1605 if (bufferSize > 1) 1606 { 1607 var exponentBuffer = stackalloc byte[5]; 1608 exponentBuffer[0] = (byte)'e'; 1609 if (printExponent >= 0) 1610 { 1611 exponentBuffer[1] = (byte)'+'; 1612 } 1613 else 1614 { 1615 exponentBuffer[1] = (byte)'-'; 1616 printExponent = -printExponent; 1617 } 1618 1619 //RJ_ASSERT(printExponent < 1000); 1620 uint hundredsPlace = (uint)(printExponent / 100); 1621 uint tensPlace = (uint)((printExponent - hundredsPlace * 100) / 10); 1622 uint onesPlace = (uint)((printExponent - hundredsPlace * 100 - tensPlace * 10)); 1623 1624 exponentBuffer[2] = (byte)('0' + hundredsPlace); 1625 exponentBuffer[3] = (byte)('0' + tensPlace); 1626 exponentBuffer[4] = (byte)('0' + onesPlace); 1627 1628 // copy the exponent buffer into the output 1629 uint maxExponentSize = bufferSize - 1; 1630 uint exponentSize = (5 < maxExponentSize) ? 5 : maxExponentSize; 1631 Unsafe.CopyBlock(pCurOut, exponentBuffer, exponentSize); 1632 pCurOut += exponentSize; 1633 bufferSize -= exponentSize; 1634 } 1635 1636 //RJ_ASSERT(bufferSize > 0); 1637 //pCurOut[0] = '\0'; 1638 1639 return (int)(pCurOut - pOutBuffer); 1640 } 1641 1642 //****************************************************************************** 1643 // Print special case values for infinities and NaNs. 1644 // The output string is always NUL terminated and the string length (not 1645 // including the NUL) is returned. 1646 //****************************************************************************** 1647 private static readonly byte[] InfinityString = new byte[] 1648 { 1649 (byte) 'I', 1650 (byte) 'n', 1651 (byte) 'f', 1652 (byte) 'i', 1653 (byte) 'n', 1654 (byte) 'i', 1655 (byte) 't', 1656 (byte) 'y', 1657 }; 1658 1659 private static readonly byte[] NanString = new byte[] 1660 { 1661 (byte) 'N', 1662 (byte) 'a', 1663 (byte) 'N', 1664 }; 1665 1666 private static unsafe void FormatInfinityNaN(byte* dest, ref int destIndex, int destLength, ulong mantissa, bool isNegative, FormatOptions formatOptions) 1667 { 1668 //RJ_ASSERT(bufferSize > 0); 1669 int length = mantissa == 0 ? 8 + (isNegative ? 1 : 0) : 3; 1670 int align = formatOptions.AlignAndSize; 1671 1672 // left align 1673 if (AlignLeft(dest, ref destIndex, destLength, align, length)) return; 1674 1675 // Check for infinity 1676 if (mantissa == 0) 1677 { 1678 if (isNegative) 1679 { 1680 if (destIndex >= destLength) return; 1681 dest[destIndex++] = (byte)'-'; 1682 } 1683 1684 for (int i = 0; i < 8; i++) 1685 { 1686 if (destIndex >= destLength) return; 1687 dest[destIndex++] = InfinityString[i]; 1688 } 1689 } 1690 else 1691 { 1692 for (int i = 0; i < 3; i++) 1693 { 1694 if (destIndex >= destLength) return; 1695 dest[destIndex++] = NanString[i]; 1696 } 1697 } 1698 1699 // right align 1700 AlignRight(dest, ref destIndex, destLength, align, length); 1701 } 1702 1703 // ------------------------------------------------------------------------------ 1704 // Part of the following code is taking some constants and code from 1705 // https://github.com/dotnet/runtime/blob/75036ffec9473dd1d948c052c041fdedd7784ac9/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs 1706 // Licensed to the .NET Foundation under one or more agreements. 1707 // The .NET Foundation licenses this file to you under the MIT license. 1708 // See the https://github.com/dotnet/runtime/blob/master/LICENSE.TXT file for more information. 1709 // ------------------------------------------------------------------------------ 1710 1711 // SinglePrecision and DoublePrecision represent the maximum number of digits required 1712 // to guarantee that any given Single or Double can roundtrip. Some numbers may require 1713 // less, but none will require more. 1714 private const int SinglePrecision = 9; 1715 private const int DoublePrecision = 17; 1716 1717 internal const int SingleNumberBufferLength = SinglePrecision + 1; // + zero 1718 internal const int DoubleNumberBufferLength = DoublePrecision + 1; // + zero 1719 1720 // SinglePrecisionCustomFormat and DoublePrecisionCustomFormat are used to ensure that 1721 // custom format strings return the same string as in previous releases when the format 1722 // would return x digits or less (where x is the value of the corresponding constant). 1723 // In order to support more digits, we would need to update ParseFormatSpecifier to pre-parse 1724 // the format and determine exactly how many digits are being requested and whether they 1725 // represent "significant digits" or "digits after the decimal point". 1726 private const int SinglePrecisionCustomFormat = 7; 1727 private const int DoublePrecisionCustomFormat = 15; 1728 1729 /// <summary> 1730 /// Format a float 32-bit to a general format to the specified destination buffer. 1731 /// </summary> 1732 /// <param name="dest">Destination buffer.</param> 1733 /// <param name="destIndex">Current index in destination buffer.</param> 1734 /// <param name="destLength">Maximum length of destination buffer.</param> 1735 /// <param name="value">The float 32 value to format.</param> 1736 /// <param name="formatOptions">Formatting options.</param> 1737 [MethodImpl(MethodImplOptions.NoInlining)] 1738 private static unsafe void ConvertFloatToString(byte* dest, ref int destIndex, int destLength, float value, FormatOptions formatOptions) 1739 { 1740 // deconstruct the floating point value 1741 tFloatUnion32 floatUnion = default; 1742 floatUnion.m_floatingPoint = value; 1743 uint floatExponent = floatUnion.GetExponent(); 1744 uint floatMantissa = floatUnion.GetMantissa(); 1745 1746 // if this is a special value 1747 if (floatExponent == 0xFF) 1748 { 1749 FormatInfinityNaN(dest, ref destIndex, destLength, floatMantissa, floatUnion.IsNegative(), formatOptions); 1750 } 1751 // else this is a number 1752 else 1753 { 1754 // factor the value into its parts 1755 uint mantissa; 1756 int exponent; 1757 uint mantissaHighBitIdx; 1758 bool hasUnequalMargins; 1759 if (floatExponent != 0) 1760 { 1761 // normalized 1762 // The floating point equation is: 1763 // value = (1 + mantissa/2^23) * 2 ^ (exponent-127) 1764 // We convert the integer equation by factoring a 2^23 out of the exponent 1765 // value = (1 + mantissa/2^23) * 2^23 * 2 ^ (exponent-127-23) 1766 // value = (2^23 + mantissa) * 2 ^ (exponent-127-23) 1767 // Because of the implied 1 in front of the mantissa we have 24 bits of precision. 1768 // m = (2^23 + mantissa) 1769 // e = (exponent-127-23) 1770 mantissa = (uint)((1UL << 23) | floatMantissa); 1771 exponent = (int)(floatExponent - 127 - 23); 1772 mantissaHighBitIdx = 23; 1773 hasUnequalMargins = (floatExponent != 1) && (floatMantissa == 0); 1774 } 1775 else 1776 { 1777 // denormalized 1778 // The floating point equation is: 1779 // value = (mantissa/2^23) * 2 ^ (1-127) 1780 // We convert the integer equation by factoring a 2^23 out of the exponent 1781 // value = (mantissa/2^23) * 2^23 * 2 ^ (1-127-23) 1782 // value = mantissa * 2 ^ (1-127-23) 1783 // We have up to 23 bits of precision. 1784 // m = (mantissa) 1785 // e = (1-127-23) 1786 mantissa = floatMantissa; 1787 exponent = 1 - 127 - 23; 1788 mantissaHighBitIdx = LogBase2(mantissa); 1789 hasUnequalMargins = false; 1790 } 1791 1792 var precision = formatOptions.Specifier == 0 ? -1 : formatOptions.Specifier; 1793 var bufferSize = Math.Max(SingleNumberBufferLength, precision + 1); 1794 1795 var pOutBuffer = stackalloc byte[bufferSize]; 1796 if (precision < 0) 1797 { 1798 precision = SinglePrecisionCustomFormat; 1799 } 1800 1801 int printExponent; 1802 uint numPrintDigits = Dragon4(mantissa, 1803 exponent, 1804 mantissaHighBitIdx, 1805 hasUnequalMargins, 1806 CutoffMode.TotalLength, 1807 (uint)precision, 1808 pOutBuffer, 1809 (uint)(bufferSize - 1), 1810 out printExponent); 1811 1812 pOutBuffer[numPrintDigits] = 0; 1813 1814 // Negative 0 are displayed as 0 1815 bool isNegative = floatUnion.IsNegative(); 1816 if (floatUnion.m_integer == ((uint)1 << 31)) 1817 { 1818 isNegative = false; 1819 } 1820 1821 var number = new NumberBuffer(NumberBufferKind.Float, pOutBuffer, (int)numPrintDigits, printExponent + 1, isNegative); 1822 FormatNumber(dest, ref destIndex, destLength, ref number, precision, formatOptions); 1823 } 1824 } 1825 1826 /// <summary> 1827 /// Format a float 64-bit to a general format to the specified destination buffer. 1828 /// </summary> 1829 /// <param name="dest">Destination buffer.</param> 1830 /// <param name="destIndex">Current index in destination buffer.</param> 1831 /// <param name="destLength">Maximum length of destination buffer.</param> 1832 /// <param name="value">The float 64 value to format.</param> 1833 /// <param name="formatOptions">Formatting options.</param> 1834 [MethodImpl(MethodImplOptions.NoInlining)] 1835 private static unsafe void ConvertDoubleToString(byte* dest, ref int destIndex, int destLength, double value, FormatOptions formatOptions) 1836 { 1837 // deconstruct the floating point value 1838 tFloatUnion64 floatUnion = default; 1839 floatUnion.m_floatingPoint = value; 1840 uint floatExponent = floatUnion.GetExponent(); 1841 ulong floatMantissa = floatUnion.GetMantissa(); 1842 1843 // if this is a special value 1844 if (floatExponent == 0x7FF) 1845 { 1846 FormatInfinityNaN(dest, ref destIndex, destLength, floatMantissa, floatUnion.IsNegative(), formatOptions); 1847 } 1848 // else this is a number 1849 else 1850 { 1851 // factor the value into its parts 1852 ulong mantissa; 1853 int exponent; 1854 uint mantissaHighBitIdx; 1855 bool hasUnequalMargins; 1856 1857 if (floatExponent != 0) 1858 { 1859 // normal 1860 // The floating point equation is: 1861 // value = (1 + mantissa/2^52) * 2 ^ (exponent-1023) 1862 // We convert the integer equation by factoring a 2^52 out of the exponent 1863 // value = (1 + mantissa/2^52) * 2^52 * 2 ^ (exponent-1023-52) 1864 // value = (2^52 + mantissa) * 2 ^ (exponent-1023-52) 1865 // Because of the implied 1 in front of the mantissa we have 53 bits of precision. 1866 // m = (2^52 + mantissa) 1867 // e = (exponent-1023+1-53) 1868 mantissa = (1UL << 52) | floatMantissa; 1869 exponent = (int)(floatExponent - 1023 - 52); 1870 mantissaHighBitIdx = 52; 1871 hasUnequalMargins = (floatExponent != 1) && (floatMantissa == 0); 1872 } 1873 else 1874 { 1875 // subnormal 1876 // The floating point equation is: 1877 // value = (mantissa/2^52) * 2 ^ (1-1023) 1878 // We convert the integer equation by factoring a 2^52 out of the exponent 1879 // value = (mantissa/2^52) * 2^52 * 2 ^ (1-1023-52) 1880 // value = mantissa * 2 ^ (1-1023-52) 1881 // We have up to 52 bits of precision. 1882 // m = (mantissa) 1883 // e = (1-1023-52) 1884 mantissa = floatMantissa; 1885 exponent = 1 - 1023 - 52; 1886 mantissaHighBitIdx = LogBase2((uint)mantissa); 1887 hasUnequalMargins = false; 1888 } 1889 1890 var precision = formatOptions.Specifier == 0 ? -1 : formatOptions.Specifier; 1891 var bufferSize = Math.Max(DoubleNumberBufferLength, precision + 1); 1892 1893 var pOutBuffer = stackalloc byte[bufferSize]; 1894 if (precision < 0) 1895 { 1896 precision = DoublePrecisionCustomFormat; 1897 } 1898 1899 int printExponent; 1900 uint numPrintDigits = Dragon4(mantissa, 1901 exponent, 1902 mantissaHighBitIdx, 1903 hasUnequalMargins, 1904 CutoffMode.TotalLength, 1905 (uint)precision, 1906 pOutBuffer, 1907 (uint)(bufferSize - 1), 1908 out printExponent); 1909 1910 pOutBuffer[numPrintDigits] = 0; 1911 1912 // Negative 0 are displayed as 0 1913 bool isNegative = floatUnion.IsNegative(); 1914 if (floatUnion.m_integer == ((ulong)1 << 63)) 1915 { 1916 isNegative = false; 1917 } 1918 1919 var number = new NumberBuffer(NumberBufferKind.Float, pOutBuffer, (int)numPrintDigits, printExponent + 1, isNegative); 1920 FormatNumber(dest, ref destIndex, destLength, ref number, precision, formatOptions); 1921 } 1922 } 1923 } 1924}