Number.Dragon4.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Diagnostics;
  5. using Internal.Runtime.CompilerServices;
  6. namespace System
  7. {
  8. // This is a port of the `Dragon4` implementation here: http://www.ryanjuckett.com/programming/printing-floating-point-numbers/part-2/
  9. // The backing algorithm and the proofs behind it are described in more detail here: https://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf
  10. internal static partial class Number
  11. {
  12. public static void Dragon4Double(double value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number)
  13. {
  14. double v = double.IsNegative(value) ? -value : value;
  15. Debug.Assert(v > 0);
  16. Debug.Assert(double.IsFinite(v));
  17. ulong mantissa = ExtractFractionAndBiasedExponent(value, out int exponent);
  18. uint mantissaHighBitIdx = 0;
  19. bool hasUnequalMargins = false;
  20. if ((mantissa >> DiyFp.DoubleImplicitBitIndex) != 0)
  21. {
  22. mantissaHighBitIdx = DiyFp.DoubleImplicitBitIndex;
  23. hasUnequalMargins = (mantissa == (1UL << DiyFp.DoubleImplicitBitIndex));
  24. }
  25. else
  26. {
  27. Debug.Assert(mantissa != 0);
  28. mantissaHighBitIdx = (uint)BitOps.Log2(mantissa);
  29. }
  30. int length = (int)(Dragon4(mantissa, exponent, mantissaHighBitIdx, hasUnequalMargins, cutoffNumber, isSignificantDigits, number.Digits, out int decimalExponent));
  31. number.Scale = decimalExponent + 1;
  32. number.Digits[length] = (byte)('\0');
  33. number.DigitsCount = length;
  34. }
  35. public static unsafe void Dragon4Single(float value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number)
  36. {
  37. float v = float.IsNegative(value) ? -value : value;
  38. Debug.Assert(v > 0);
  39. Debug.Assert(float.IsFinite(v));
  40. uint mantissa = ExtractFractionAndBiasedExponent(value, out int exponent);
  41. uint mantissaHighBitIdx = 0;
  42. bool hasUnequalMargins = false;
  43. if ((mantissa >> DiyFp.SingleImplicitBitIndex) != 0)
  44. {
  45. mantissaHighBitIdx = DiyFp.SingleImplicitBitIndex;
  46. hasUnequalMargins = (mantissa == (1U << DiyFp.SingleImplicitBitIndex));
  47. }
  48. else
  49. {
  50. Debug.Assert(mantissa != 0);
  51. mantissaHighBitIdx = (uint)BitOps.Log2(mantissa);
  52. }
  53. int length = (int)(Dragon4(mantissa, exponent, mantissaHighBitIdx, hasUnequalMargins, cutoffNumber, isSignificantDigits, number.Digits, out int decimalExponent));
  54. number.Scale = decimalExponent + 1;
  55. number.Digits[length] = (byte)('\0');
  56. number.DigitsCount = length;
  57. }
  58. // This is an implementation of the Dragon4 algorithm to convert a binary number in floating-point format to a decimal number in string format.
  59. // The function returns the number of digits written to the output buffer and the output is not NUL terminated.
  60. //
  61. // The floating point input value is (mantissa * 2^exponent).
  62. //
  63. // See the following papers for more information on the algorithm:
  64. // "How to Print Floating-Point Numbers Accurately"
  65. // Steele and White
  66. // http://kurtstephens.com/files/p372-steele.pdf
  67. // "Printing Floating-Point Numbers Quickly and Accurately"
  68. // Burger and Dybvig
  69. // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.4656&rep=rep1&type=pdf
  70. private static unsafe uint Dragon4(ulong mantissa, int exponent, uint mantissaHighBitIdx, bool hasUnequalMargins, int cutoffNumber, bool isSignificantDigits, Span<byte> buffer, out int decimalExponent)
  71. {
  72. int curDigit = 0;
  73. Debug.Assert(buffer.Length > 0);
  74. // We deviate from the original algorithm and just assert that the mantissa
  75. // is not zero. Comparing to zero is fine since the caller should have set
  76. // the implicit bit of the mantissa, meaning it would only ever be zero if
  77. // the extracted exponent was also zero. And the assertion is fine since we
  78. // require that the DoubleToNumber handle zero itself.
  79. Debug.Assert(mantissa != 0);
  80. // Compute the initial state in integral form such that
  81. // value = scaledValue / scale
  82. // marginLow = scaledMarginLow / scale
  83. BigInteger scale; // positive scale applied to value and margin such that they can be represented as whole numbers
  84. BigInteger scaledValue; // scale * mantissa
  85. BigInteger scaledMarginLow; // scale * 0.5 * (distance between this floating-point number and its immediate lower value)
  86. // For normalized IEEE floating-point values, each time the exponent is incremented the margin also doubles.
  87. // That creates a subset of transition numbers where the high margin is twice the size of the low margin.
  88. BigInteger* pScaledMarginHigh;
  89. BigInteger optionalMarginHigh;
  90. if (hasUnequalMargins)
  91. {
  92. if (exponent > 0) // We have no fractional component
  93. {
  94. // 1) Expand the input value by multiplying out the mantissa and exponent.
  95. // This represents the input value in its whole number representation.
  96. // 2) Apply an additional scale of 2 such that later comparisons against the margin values are simplified.
  97. // 3) Set the margin value to the loweset mantissa bit's scale.
  98. // scaledValue = 2 * 2 * mantissa * 2^exponent
  99. scaledValue = new BigInteger(4 * mantissa);
  100. scaledValue.ShiftLeft((uint)(exponent));
  101. // scale = 2 * 2 * 1
  102. scale = new BigInteger(4);
  103. // scaledMarginLow = 2 * 2^(exponent - 1)
  104. BigInteger.Pow2((uint)(exponent), out scaledMarginLow);
  105. // scaledMarginHigh = 2 * 2 * 2^(exponent + 1)
  106. BigInteger.Pow2((uint)(exponent + 1), out optionalMarginHigh);
  107. }
  108. else // We have a fractional exponent
  109. {
  110. // In order to track the mantissa data as an integer, we store it as is with a large scale
  111. // scaledValue = 2 * 2 * mantissa
  112. scaledValue = new BigInteger(4 * mantissa);
  113. // scale = 2 * 2 * 2^(-exponent)
  114. BigInteger.Pow2((uint)(-exponent + 2), out scale);
  115. // scaledMarginLow = 2 * 2^(-1)
  116. scaledMarginLow = new BigInteger(1);
  117. // scaledMarginHigh = 2 * 2 * 2^(-1)
  118. optionalMarginHigh = new BigInteger(2);
  119. }
  120. // The high and low margins are different
  121. pScaledMarginHigh = &optionalMarginHigh;
  122. }
  123. else
  124. {
  125. if (exponent > 0) // We have no fractional component
  126. {
  127. // 1) Expand the input value by multiplying out the mantissa and exponent.
  128. // This represents the input value in its whole number representation.
  129. // 2) Apply an additional scale of 2 such that later comparisons against the margin values are simplified.
  130. // 3) Set the margin value to the lowest mantissa bit's scale.
  131. // scaledValue = 2 * mantissa*2^exponent
  132. scaledValue = new BigInteger(2 * mantissa);
  133. scaledValue.ShiftLeft((uint)(exponent));
  134. // scale = 2 * 1
  135. scale = new BigInteger(2);
  136. // scaledMarginLow = 2 * 2^(exponent-1)
  137. BigInteger.Pow2((uint)(exponent), out scaledMarginLow);
  138. }
  139. else // We have a fractional exponent
  140. {
  141. // In order to track the mantissa data as an integer, we store it as is with a large scale
  142. // scaledValue = 2 * mantissa
  143. scaledValue = new BigInteger(2 * mantissa);
  144. // scale = 2 * 2^(-exponent)
  145. BigInteger.Pow2((uint)(-exponent + 1), out scale);
  146. // scaledMarginLow = 2 * 2^(-1)
  147. scaledMarginLow = new BigInteger(1);
  148. }
  149. // The high and low margins are equal
  150. pScaledMarginHigh = &scaledMarginLow;
  151. }
  152. // Compute an estimate for digitExponent that will be correct or undershoot by one.
  153. //
  154. // This optimization is based on the paper "Printing Floating-Point Numbers Quickly and Accurately" by Burger and Dybvig http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.4656&rep=rep1&type=pdf
  155. //
  156. // We perform an additional subtraction of 0.69 to increase the frequency of a failed estimate because that lets us take a faster branch in the code.
  157. // 0.69 is chosen because 0.69 + log10(2) is less than one by a reasonable epsilon that will account for any floating point error.
  158. //
  159. // We want to set digitExponent to floor(log10(v)) + 1
  160. // v = mantissa * 2^exponent
  161. // log2(v) = log2(mantissa) + exponent;
  162. // log10(v) = log2(v) * log10(2)
  163. // floor(log2(v)) = mantissaHighBitIdx + exponent;
  164. // log10(v) - log10(2) < (mantissaHighBitIdx + exponent) * log10(2) <= log10(v)
  165. // log10(v) < (mantissaHighBitIdx + exponent) * log10(2) + log10(2) <= log10(v) + log10(2)
  166. // floor(log10(v)) < ceil((mantissaHighBitIdx + exponent) * log10(2)) <= floor(log10(v)) + 1
  167. const double Log10V2 = 0.30102999566398119521373889472449;
  168. int digitExponent = (int)(Math.Ceiling(((int)(mantissaHighBitIdx) + exponent) * Log10V2 - 0.69));
  169. // Divide value by 10^digitExponent.
  170. if (digitExponent > 0)
  171. {
  172. // The exponent is positive creating a division so we multiply up the scale.
  173. scale.MultiplyPow10((uint)(digitExponent));
  174. }
  175. else if (digitExponent < 0)
  176. {
  177. // The exponent is negative creating a multiplication so we multiply up the scaledValue, scaledMarginLow and scaledMarginHigh.
  178. BigInteger.Pow10((uint)(-digitExponent), out BigInteger pow10);
  179. scaledValue.Multiply(ref pow10);
  180. scaledMarginLow.Multiply(ref pow10);
  181. if (pScaledMarginHigh != &scaledMarginLow)
  182. {
  183. BigInteger.Multiply(ref scaledMarginLow, 2, ref *pScaledMarginHigh);
  184. }
  185. }
  186. // If (value >= 1), our estimate for digitExponent was too low
  187. if (BigInteger.Compare(ref scaledValue, ref scale) >= 0)
  188. {
  189. // The exponent estimate was incorrect.
  190. // Increment the exponent and don't perform the premultiply needed for the first loop iteration.
  191. digitExponent = digitExponent + 1;
  192. }
  193. else
  194. {
  195. // The exponent estimate was correct.
  196. // Multiply larger by the output base to prepare for the first loop iteration.
  197. scaledValue.Multiply10();
  198. scaledMarginLow.Multiply10();
  199. if (pScaledMarginHigh != &scaledMarginLow)
  200. {
  201. BigInteger.Multiply(ref scaledMarginLow, 2, ref *pScaledMarginHigh);
  202. }
  203. }
  204. // Compute the cutoff exponent (the exponent of the final digit to print).
  205. // Default to the maximum size of the output buffer.
  206. int cutoffExponent = digitExponent - buffer.Length;
  207. if (cutoffNumber != -1)
  208. {
  209. int desiredCutoffExponent = 0;
  210. if (isSignificantDigits)
  211. {
  212. // We asked for a specific number of significant digits.
  213. Debug.Assert(cutoffNumber > 0);
  214. desiredCutoffExponent = digitExponent - cutoffNumber;
  215. }
  216. else
  217. {
  218. // We asked for a specific number of fractional digits.
  219. Debug.Assert(cutoffNumber >= 0);
  220. desiredCutoffExponent = -cutoffNumber;
  221. }
  222. if (desiredCutoffExponent > cutoffExponent)
  223. {
  224. // Only select the new cutoffExponent if it won't overflow the destination buffer.
  225. cutoffExponent = desiredCutoffExponent;
  226. }
  227. }
  228. // Output the exponent of the first digit we will print
  229. decimalExponent = digitExponent - 1;
  230. // In preparation for calling BigInteger.HeuristicDivie(), we need to scale up our values such that the highest block of the denominator is greater than or equal to 8.
  231. // We also need to guarantee that the numerator can never have a length greater than the denominator after each loop iteration.
  232. // This requires the highest block of the denominator to be less than or equal to 429496729 which is the highest number that can be multiplied by 10 without overflowing to a new block.
  233. Debug.Assert(scale.GetLength() > 0);
  234. uint hiBlock = scale.GetBlock((uint)(scale.GetLength() - 1));
  235. if ((hiBlock < 8) || (hiBlock > 429496729))
  236. {
  237. // Perform a bit shift on all values to get the highest block of the denominator into the range [8,429496729].
  238. // We are more likely to make accurate quotient estimations in BigInteger.HeuristicDivide() with higher denominator values so we shift the denominator to place the highest bit at index 27 of the highest block.
  239. // This is safe because (2^28 - 1) = 268435455 which is less than 429496729.
  240. // This means that all values with a highest bit at index 27 are within range.
  241. Debug.Assert(hiBlock != 0);
  242. uint hiBlockLog2 = (uint)BitOps.Log2(hiBlock);
  243. Debug.Assert((hiBlockLog2 < 3) || (hiBlockLog2 > 27));
  244. uint shift = (32 + 27 - hiBlockLog2) % 32;
  245. scale.ShiftLeft(shift);
  246. scaledValue.ShiftLeft(shift);
  247. scaledMarginLow.ShiftLeft(shift);
  248. if (pScaledMarginHigh != &scaledMarginLow)
  249. {
  250. BigInteger.Multiply(ref scaledMarginLow, 2, ref *pScaledMarginHigh);
  251. }
  252. }
  253. // These values are used to inspect why the print loop terminated so we can properly round the final digit.
  254. bool low; // did the value get within marginLow distance from zero
  255. bool high; // did the value get within marginHigh distance from one
  256. uint outputDigit; // current digit being output
  257. if (cutoffNumber == -1)
  258. {
  259. Debug.Assert(isSignificantDigits);
  260. // For the unique cutoff mode, we will try to print until we have reached a level of precision that uniquely distinguishes this value from its neighbors.
  261. // If we run out of space in the output buffer, we terminate early.
  262. while (true)
  263. {
  264. digitExponent = digitExponent - 1;
  265. // divide out the scale to extract the digit
  266. outputDigit = BigInteger.HeuristicDivide(ref scaledValue, ref scale);
  267. Debug.Assert(outputDigit < 10);
  268. // update the high end of the value
  269. BigInteger.Add(ref scaledValue, ref *pScaledMarginHigh, out BigInteger scaledValueHigh);
  270. // stop looping if we are far enough away from our neighboring values or if we have reached the cutoff digit
  271. low = BigInteger.Compare(ref scaledValue, ref scaledMarginLow) < 0;
  272. high = BigInteger.Compare(ref scaledValueHigh, ref scale) > 0;
  273. if (low || high || (digitExponent == cutoffExponent))
  274. {
  275. break;
  276. }
  277. // store the output digit
  278. buffer[curDigit] = (byte)('0' + outputDigit);
  279. curDigit += 1;
  280. // multiply larger by the output base
  281. scaledValue.Multiply10();
  282. scaledMarginLow.Multiply10();
  283. if (pScaledMarginHigh != &scaledMarginLow)
  284. {
  285. BigInteger.Multiply(ref scaledMarginLow, 2, ref *pScaledMarginHigh);
  286. }
  287. }
  288. }
  289. else
  290. {
  291. Debug.Assert((cutoffNumber > 0) || ((cutoffNumber == 0) && !isSignificantDigits));
  292. // For length based cutoff modes, we will try to print until we have exhausted all precision (i.e. all remaining digits are zeros) or until we reach the desired cutoff digit.
  293. low = false;
  294. high = false;
  295. while (true)
  296. {
  297. digitExponent = digitExponent - 1;
  298. // divide out the scale to extract the digit
  299. outputDigit = BigInteger.HeuristicDivide(ref scaledValue, ref scale);
  300. Debug.Assert(outputDigit < 10);
  301. if (scaledValue.IsZero() || (digitExponent <= cutoffExponent))
  302. {
  303. break;
  304. }
  305. // store the output digit
  306. buffer[curDigit] = (byte)('0' + outputDigit);
  307. curDigit += 1;
  308. // multiply larger by the output base
  309. scaledValue.Multiply10();
  310. }
  311. }
  312. // round off the final digit
  313. // default to rounding down if value got too close to 0
  314. bool roundDown = low;
  315. if (low == high) // is it legal to round up and down
  316. {
  317. // round to the closest digit by comparing value with 0.5.
  318. //
  319. // To do this we need to convert the inequality to large integer values.
  320. // compare(value, 0.5)
  321. // compare(scale * value, scale * 0.5)
  322. // compare(2 * scale * value, scale)
  323. scaledValue.Multiply(2);
  324. int compare = BigInteger.Compare(ref scaledValue, ref scale);
  325. roundDown = compare < 0;
  326. // if we are directly in the middle, round towards the even digit (i.e. IEEE rouding rules)
  327. if (compare == 0)
  328. {
  329. roundDown = (outputDigit & 1) == 0;
  330. }
  331. }
  332. // print the rounded digit
  333. if (roundDown)
  334. {
  335. buffer[curDigit] = (byte)('0' + outputDigit);
  336. curDigit += 1;
  337. }
  338. else if (outputDigit == 9) // handle rounding up
  339. {
  340. // find the first non-nine prior digit
  341. while (true)
  342. {
  343. // if we are at the first digit
  344. if (curDigit == 0)
  345. {
  346. // output 1 at the next highest exponent
  347. buffer[curDigit] = (byte)('1');
  348. curDigit += 1;
  349. decimalExponent += 1;
  350. break;
  351. }
  352. curDigit -= 1;
  353. if (buffer[curDigit] != '9')
  354. {
  355. // increment the digit
  356. buffer[curDigit] += 1;
  357. curDigit += 1;
  358. break;
  359. }
  360. }
  361. }
  362. else
  363. {
  364. // values in the range [0,8] can perform a simple round up
  365. buffer[curDigit] = (byte)('0' + outputDigit + 1);
  366. curDigit += 1;
  367. }
  368. // return the number of digits output
  369. uint outputLen = (uint)(curDigit);
  370. Debug.Assert(outputLen <= buffer.Length);
  371. return outputLen;
  372. }
  373. }
  374. }