1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Diagnostics;
9 internal static partial class Number
11 internal static unsafe class Grisu3
13 private const int Alpha = -59;
14 private const double D1Log210 = 0.301029995663981195;
15 private const int Gamma = -32;
16 private const int PowerDecimalExponentDistance = 8;
17 private const int PowerMinDecimalExponent = -348;
18 private const int PowerMaxDecimalExponent = 340;
19 private const int PowerOffset = -PowerMinDecimalExponent;
20 private const uint Ten4 = 10000;
21 private const uint Ten5 = 100000;
22 private const uint Ten6 = 1000000;
23 private const uint Ten7 = 10000000;
24 private const uint Ten8 = 100000000;
25 private const uint Ten9 = 1000000000;
27 private static readonly short[] s_CachedPowerBinaryExponents = new short[]
118 private static readonly short[] s_CachedPowerDecimalExponents = new short[]
120 PowerMinDecimalExponent,
206 PowerMaxDecimalExponent,
209 private static readonly uint[] s_CachedPowerOfTen = new uint[]
223 private static readonly ulong[] s_CachedPowerSignificands = new ulong[]
314 public static bool Run(double value, int precision, ref NumberBuffer number)
316 // ========================================================================================================================================
317 // This implementation is based on the paper: http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
318 // You must read this paper to fully understand the code.
320 // Deviation: Instead of generating shortest digits, we generate the digits according to the input count.
321 // Therefore, we do not need m+ and m- which are used to determine the exact range of values.
322 // ========================================================================================================================================
326 // The idea of Grisu3 is to leverage additional bits and cached power of ten to produce the digits.
327 // We need to create a handmade floating point data structure DiyFp to extend the bits of double.
328 // We also need to cache the powers of ten for digits generation. By choosing the correct index of powers
329 // we need to start with, we can eliminate the expensive big num divide operation.
331 // Grisu3 is imprecision for some numbers. Fortunately, the algorithm itself can determine that and give us
332 // a success/fail flag. We may fall back to other algorithms (For instance, Dragon4) if it fails.
334 // w: the normalized DiyFp from the input value.
335 // mk: The index of the cached powers.
336 // cmk: The cached power.
337 // D: Product: w * cmk.
338 // kappa: A factor used for generating digits. See step 5 of the Grisu3 procedure in the paper.
341 if (double.IsNegative(value))
351 // Step 1: Determine the normalized DiyFp w.
353 DiyFp.GenerateNormalizedDiyFp(value, out DiyFp w);
355 // Step 2: Find the cached power of ten.
357 // Compute the proper index mk.
358 int mk = KComp(w.e + DiyFp.SignificandLength);
360 // Retrieve the cached power of ten.
361 CachedPower(mk, out DiyFp cmk, out int decimalExponent);
363 // Step 3: Scale the w with the cached power of ten.
365 DiyFp.Multiply(ref w, ref cmk, out DiyFp D);
367 // Step 4: Generate digits.
369 bool isSuccess = DigitGen(ref D, precision, number.GetDigitsPointer(), out int length, out int kappa);
373 number.digits[precision] = '\0';
374 number.scale = (length - decimalExponent + kappa);
380 // Returns the biggest power of ten that is less than or equal to the given number.
381 static void BiggestPowerTenLessThanOrEqualTo(uint number, int bits, out uint power, out int exponent)
539 Debug.Fail("unreachable");
545 private static void CachedPower(int k, out DiyFp cmk, out int decimalExponent)
547 int index = ((PowerOffset + k - 1) / PowerDecimalExponentDistance) + 1;
548 cmk = new DiyFp(s_CachedPowerSignificands[index], s_CachedPowerBinaryExponents[index]);
549 decimalExponent = s_CachedPowerDecimalExponents[index];
552 private static bool DigitGen(ref DiyFp mp, int precision, char* digits, out int length, out int k)
554 // Split the input mp to two parts. Part 1 is integral. Part 2 can be used to calculate
557 // mp: the input DiyFp scaled by cached power.
562 Debug.Assert(precision > 0);
563 Debug.Assert(digits != null);
564 Debug.Assert(mp.e >= Alpha);
565 Debug.Assert(mp.e <= Gamma);
570 var one = new DiyFp(1UL << -mpE, mpE);
573 int oneNegE = -one.e;
577 uint p1 = (uint)(mpF >> oneNegE);
578 ulong p2 = mpF & (oneF - 1);
580 // When p2 (fractional part) is zero, we can predicate if p1 is good to produce the numbers in requested digit count:
582 // - When requested digit count >= 11, p1 is not be able to exhaust the count as 10^(11 - 1) > uint.MaxValue >= p1.
583 // - When p1 < 10^(count - 1), p1 is not be able to exhaust the count.
584 // - Otherwise, p1 may have chance to exhaust the count.
585 if ((p2 == 0) && ((precision >= 11) || (p1 < s_CachedPowerOfTen[precision - 1])))
592 // Note: The code in the paper simply assigns div to Ten9 and kappa to 10 directly.
593 // That means we need to check if any leading zero of the generated
594 // digits during the while loop, which hurts the performance.
596 // Now if we can estimate what the div and kappa, we do not need to check the leading zeros.
597 // The idea is to find the biggest power of 10 that is less than or equal to the given number.
598 // Then we don't need to worry about the leading zeros and we can get 10% performance gain.
600 BiggestPowerTenLessThanOrEqualTo(p1, (DiyFp.SignificandLength - oneNegE), out uint div, out int kappa);
606 int d = (int)(Math.DivRem(p1, div, out p1));
607 digits[index] = (char)('0' + d);
621 // End up here if we already exhausted the digit count.
624 ulong rest = ((ulong)(p1) << oneNegE) + p2;
633 ((ulong)(div)) << oneNegE,
639 // We have to generate digits from part2 if we have requested digit count left
640 // and part2 is greater than ulp.
641 while ((precision > 0) && (p2 > ulp))
645 int d = (int)(p2 >> oneNegE);
646 digits[index] = (char)('0' + d);
657 // If we haven't exhausted the requested digit counts, the Grisu3 algorithm fails.
668 return RoundWeed(digits, index, p2, oneF, ulp, ref k);
671 private static int KComp(int e)
673 return (int)(Math.Ceiling((Alpha - e + DiyFp.SignificandLength - 1) * D1Log210));
676 private static bool RoundWeed(char* buffer, int len, ulong rest, ulong tenKappa, ulong ulp, ref int kappa)
678 Debug.Assert(rest < tenKappa);
680 // 1. tenKappa <= ulp: we don't have an idea which way to round.
681 // 2. Even if tenKappa > ulp, but if tenKappa <= 2 * ulp we cannot find the way to round.
682 // Note: to prevent overflow, we need to use tenKappa - ulp <= ulp.
683 if ((tenKappa <= ulp) || ((tenKappa - ulp) <= ulp))
688 // tenKappa >= 2 * (rest + ulp). We should round down.
689 // Note: to prevent overflow, we need to check if tenKappa > 2 * rest as a prerequisite.
690 if (((tenKappa - rest) > rest) && ((tenKappa - (2 * rest)) >= (2 * ulp)))
695 // tenKappa <= 2 * (rest - ulp). We should round up.
696 // Note: to prevent overflow, we need to check if rest > ulp as a prerequisite.
697 if ((rest > ulp) && (tenKappa <= (rest - ulp) || ((tenKappa - (rest - ulp)) <= (rest - ulp))))
699 // Find all 9s from end to start.
701 for (int i = len - 1; i > 0; i--)
703 if (buffer[i] != (char)('0' + 10))
705 // We end up a number less than 9.
709 // Current number becomes 0 and add the promotion to the next number.
714 if (buffer[0] == (char)('0' + 10))
716 // First number is '0' + 10 means all numbers are 9.
717 // We simply make the first number to 1 and increase the kappa.