A game about forced loneliness, made by TACStudios
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}