Number.DiyFp.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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. namespace System
  6. {
  7. internal static partial class Number
  8. {
  9. // An exteneded floating-point data structure which is required by Grisu3 algorithm.
  10. // It defines a 64-bit significand and a 32-bit exponent,
  11. // which is EXTENDED compare to IEEE double precision floating-point number (53 bits significand and 11 bits exponent).
  12. //
  13. // Original Grisu algorithm produces suboptimal results. To shorten the output (which is part of Grisu2/Grisu3's job),
  14. // we need additional 11 bits of the significand f and larger exponent e (A larger exponent range is used to avoid overflow. A 32-bit exponent is far big enough).
  15. // To fully understand how Grisu3 uses this data structure to get better result, please read http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
  16. internal struct DiyFp
  17. {
  18. public const int SignificandLength = 64;
  19. // Extended significand.
  20. // IEEE 754 double-precision numbers only require 53 bits significand.
  21. // However, in Grisu3 we need additional 11 bits so we declare _f as ulong.
  22. // Please note _f does not include sign bit.
  23. private ulong _f;
  24. // Extended exponent.
  25. // IEEE 754 double-precision numbers only require 11 bits exponent.
  26. // However, in Grisu3 we need extra space to avoid overflow so we declare _e as int.
  27. // Please note _e is a biased exponent if the DiyFp instance is generated by GenerateNormalizedDiyFp().
  28. private int _e;
  29. public DiyFp(ulong f, int e)
  30. {
  31. _f = f;
  32. _e = e;
  33. }
  34. public ulong f
  35. {
  36. get
  37. {
  38. return _f;
  39. }
  40. set
  41. {
  42. _f = value;
  43. }
  44. }
  45. public int e
  46. {
  47. get
  48. {
  49. return _e;
  50. }
  51. set
  52. {
  53. _e = value;
  54. }
  55. }
  56. public static void GenerateNormalizedDiyFp(double value, out DiyFp result)
  57. {
  58. Debug.Assert(value > 0.0);
  59. long f = ExtractFractionAndBiasedExponent(value, out int e);
  60. while ((f & (1L << 52)) == 0)
  61. {
  62. f <<= 1;
  63. e--;
  64. }
  65. int lengthDelta = (SignificandLength - 53);
  66. f <<= lengthDelta;
  67. e -= lengthDelta;
  68. result = new DiyFp((ulong)(f), e);
  69. }
  70. public static void Minus(ref DiyFp lhs, ref DiyFp rhs, out DiyFp result)
  71. {
  72. result = lhs;
  73. result.Minus(ref rhs);
  74. }
  75. public static void Multiply(ref DiyFp lhs, ref DiyFp rhs, out DiyFp result)
  76. {
  77. result = lhs;
  78. result.Multiply(ref rhs);
  79. }
  80. public void Minus(ref DiyFp rhs)
  81. {
  82. Debug.Assert(_e == rhs._e);
  83. Debug.Assert(_f >= rhs._f);
  84. _f -= rhs._f;
  85. }
  86. public void Multiply(ref DiyFp rhs)
  87. {
  88. ulong lf = _f;
  89. ulong rf = rhs._f;
  90. uint a = (uint)(lf >> 32);
  91. uint b = (uint)(lf);
  92. uint c = (uint)(rf >> 32);
  93. uint d = (uint)(rf);
  94. ulong ac = ((ulong)(a) * c);
  95. ulong bc = ((ulong)(b) * c);
  96. ulong ad = ((ulong)(a) * d);
  97. ulong bd = ((ulong)(b) * d);
  98. ulong tmp = (bd >> 32) + (uint)(ad) + (uint)(bc);
  99. tmp += (1UL << 31);
  100. _f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
  101. _e += (rhs._e + SignificandLength);
  102. }
  103. }
  104. }
  105. }