BignumDtoa.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. #nullable disable
  2. using System.Diagnostics;
  3. using Jint.Runtime;
  4. namespace Jint.Native.Number.Dtoa
  5. {
  6. internal static class BignumDtoa
  7. {
  8. public static void NumberToString(
  9. double v,
  10. DtoaMode mode,
  11. int requested_digits,
  12. ref DtoaBuilder builder,
  13. out int decimal_point)
  14. {
  15. var bits = (ulong) BitConverter.DoubleToInt64Bits(v);
  16. var significand = DoubleHelper.Significand(bits);
  17. var is_even = (significand & 1) == 0;
  18. var exponent = DoubleHelper.Exponent(bits);
  19. var normalized_exponent = DoubleHelper.NormalizedExponent(significand, exponent);
  20. // estimated_power might be too low by 1.
  21. var estimated_power = EstimatePower(normalized_exponent);
  22. // Shortcut for Fixed.
  23. // The requested digits correspond to the digits after the point. If the
  24. // number is much too small, then there is no need in trying to get any
  25. // digits.
  26. if (mode == DtoaMode.Fixed && -estimated_power - 1 > requested_digits)
  27. {
  28. // Set decimal-point to -requested_digits. This is what Gay does.
  29. // Note that it should not have any effect anyways since the string is
  30. // empty.
  31. decimal_point = -requested_digits;
  32. return;
  33. }
  34. Bignum numerator = new Bignum();
  35. Bignum denominator = new Bignum();
  36. Bignum delta_minus = new Bignum();
  37. Bignum delta_plus = new Bignum();
  38. // Make sure the bignum can grow large enough. The smallest double equals
  39. // 4e-324. In this case the denominator needs fewer than 324*4 binary digits.
  40. // The maximum double is 1.7976931348623157e308 which needs fewer than
  41. // 308*4 binary digits.
  42. var need_boundary_deltas = mode == DtoaMode.Shortest;
  43. InitialScaledStartValues(
  44. v,
  45. estimated_power,
  46. need_boundary_deltas,
  47. numerator,
  48. denominator,
  49. delta_minus,
  50. delta_plus);
  51. // We now have v = (numerator / denominator) * 10^estimated_power.
  52. FixupMultiply10(
  53. estimated_power,
  54. is_even,
  55. out decimal_point,
  56. numerator,
  57. denominator,
  58. delta_minus,
  59. delta_plus);
  60. // We now have v = (numerator / denominator) * 10^(decimal_point-1), and
  61. // 1 <= (numerator + delta_plus) / denominator < 10
  62. switch (mode)
  63. {
  64. case DtoaMode.Shortest:
  65. GenerateShortestDigits(
  66. numerator,
  67. denominator,
  68. delta_minus,
  69. delta_plus,
  70. is_even,
  71. ref builder);
  72. break;
  73. case DtoaMode.Fixed:
  74. BignumToFixed(
  75. requested_digits,
  76. ref decimal_point,
  77. numerator,
  78. denominator,
  79. ref builder);
  80. break;
  81. case DtoaMode.Precision:
  82. GenerateCountedDigits(
  83. requested_digits,
  84. ref decimal_point,
  85. numerator,
  86. denominator,
  87. ref builder);
  88. break;
  89. default:
  90. ExceptionHelper.ThrowArgumentOutOfRangeException();
  91. break;
  92. }
  93. }
  94. // The procedure starts generating digits from the left to the right and stops
  95. // when the generated digits yield the shortest decimal representation of v. A
  96. // decimal representation of v is a number lying closer to v than to any other
  97. // double, so it converts to v when read.
  98. //
  99. // This is true if d, the decimal representation, is between m- and m+, the
  100. // upper and lower boundaries. d must be strictly between them if !is_even.
  101. // m- := (numerator - delta_minus) / denominator
  102. // m+ := (numerator + delta_plus) / denominator
  103. //
  104. // Precondition: 0 <= (numerator+delta_plus) / denominator < 10.
  105. // If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit
  106. // will be produced. This should be the standard precondition.
  107. private static void GenerateShortestDigits(
  108. Bignum numerator,
  109. Bignum denominator,
  110. Bignum delta_minus,
  111. Bignum delta_plus,
  112. bool is_even,
  113. ref DtoaBuilder buffer)
  114. {
  115. // Small optimization: if delta_minus and delta_plus are the same just reuse
  116. // one of the two bignums.
  117. if (Bignum.Equal(delta_minus, delta_plus))
  118. {
  119. delta_plus = delta_minus;
  120. }
  121. buffer.Reset();
  122. while (true)
  123. {
  124. uint digit;
  125. digit = numerator.DivideModuloIntBignum(denominator);
  126. Debug.Assert(digit <= 9); // digit is a uint and therefore always positive.
  127. // digit = numerator / denominator (integer division).
  128. // numerator = numerator % denominator.
  129. buffer.Append((char) (digit + '0'));
  130. // Can we stop already?
  131. // If the remainder of the division is less than the distance to the lower
  132. // boundary we can stop. In this case we simply round down (discarding the
  133. // remainder).
  134. // Similarly we test if we can round up (using the upper boundary).
  135. bool in_delta_room_minus;
  136. bool in_delta_room_plus;
  137. if (is_even)
  138. {
  139. in_delta_room_minus = Bignum.LessEqual(numerator, delta_minus);
  140. }
  141. else
  142. {
  143. in_delta_room_minus = Bignum.Less(numerator, delta_minus);
  144. }
  145. if (is_even)
  146. {
  147. in_delta_room_plus = Bignum.PlusCompare(numerator, delta_plus, denominator) >= 0;
  148. }
  149. else
  150. {
  151. in_delta_room_plus = Bignum.PlusCompare(numerator, delta_plus, denominator) > 0;
  152. }
  153. if (!in_delta_room_minus && !in_delta_room_plus)
  154. {
  155. // Prepare for next iteration.
  156. numerator.Times10();
  157. delta_minus.Times10();
  158. // We optimized delta_plus to be equal to delta_minus (if they share the
  159. // same value). So don't multiply delta_plus if they point to the same
  160. // object.
  161. if (delta_minus != delta_plus) delta_plus.Times10();
  162. }
  163. else if (in_delta_room_minus && in_delta_room_plus)
  164. {
  165. // Let's see if 2*numerator < denominator.
  166. // If yes, then the next digit would be < 5 and we can round down.
  167. int compare = Bignum.PlusCompare(numerator, numerator, denominator);
  168. if (compare < 0)
  169. {
  170. // Remaining digits are less than .5. -> Round down (== do nothing).
  171. }
  172. else if (compare > 0)
  173. {
  174. // Remaining digits are more than .5 of denominator. . Round up.
  175. // Note that the last digit could not be a '9' as otherwise the whole
  176. // loop would have stopped earlier.
  177. // We still have an assert here in case the preconditions were not
  178. // satisfied.
  179. Debug.Assert(buffer[buffer.Length - 1] != '9');
  180. buffer[buffer.Length - 1]++;
  181. }
  182. else
  183. {
  184. // Halfway case.
  185. // TODO(floitsch): need a way to solve half-way cases.
  186. // For now let's round towards even (since this is what Gay seems to
  187. // do).
  188. if ((buffer[buffer.Length - 1] - '0') % 2 == 0)
  189. {
  190. // Round down => Do nothing.
  191. }
  192. else
  193. {
  194. Debug.Assert(buffer[buffer.Length - 1] != '9');
  195. buffer[buffer.Length - 1]++;
  196. }
  197. }
  198. return;
  199. }
  200. else if (in_delta_room_minus)
  201. {
  202. // Round down (== do nothing).
  203. return;
  204. }
  205. else
  206. {
  207. // in_delta_room_plus
  208. // Round up.
  209. // Note again that the last digit could not be '9' since this would have
  210. // stopped the loop earlier.
  211. // We still have an DCHECK here, in case the preconditions were not
  212. // satisfied.
  213. Debug.Assert(buffer[buffer.Length - 1] != '9');
  214. buffer[buffer.Length - 1]++;
  215. return;
  216. }
  217. }
  218. }
  219. // Let v = numerator / denominator < 10.
  220. // Then we generate 'count' digits of d = x.xxxxx... (without the decimal point)
  221. // from left to right. Once 'count' digits have been produced we decide wether
  222. // to round up or down. Remainders of exactly .5 round upwards. Numbers such
  223. // as 9.999999 propagate a carry all the way, and change the
  224. // exponent (decimal_point), when rounding upwards.
  225. static void GenerateCountedDigits(
  226. int count,
  227. ref int decimal_point,
  228. Bignum numerator,
  229. Bignum denominator,
  230. ref DtoaBuilder buffer)
  231. {
  232. Debug.Assert(count >= 0);
  233. for (int i = 0; i < count - 1; ++i)
  234. {
  235. uint d = numerator.DivideModuloIntBignum(denominator);
  236. Debug.Assert(d <= 9); // digit is a uint and therefore always positive.
  237. // digit = numerator / denominator (integer division).
  238. // numerator = numerator % denominator.
  239. buffer.Append((char) (d + '0'));
  240. // Prepare for next iteration.
  241. numerator.Times10();
  242. }
  243. // Generate the last digit.
  244. uint digit = numerator.DivideModuloIntBignum(denominator);
  245. if (Bignum.PlusCompare(numerator, numerator, denominator) >= 0)
  246. {
  247. digit++;
  248. }
  249. buffer.Append((char) (digit + '0'));
  250. // Correct bad digits (in case we had a sequence of '9's). Propagate the
  251. // carry until we hat a non-'9' or til we reach the first digit.
  252. for (int i = count - 1; i > 0; --i)
  253. {
  254. if (buffer[i] != '0' + 10) break;
  255. buffer[i] = '0';
  256. buffer[i - 1]++;
  257. }
  258. if (buffer[0] == '0' + 10)
  259. {
  260. // Propagate a carry past the top place.
  261. buffer[0] = '1';
  262. decimal_point++;
  263. }
  264. }
  265. // Generates 'requested_digits' after the decimal point. It might omit
  266. // trailing '0's. If the input number is too small then no digits at all are
  267. // generated (ex.: 2 fixed digits for 0.00001).
  268. //
  269. // Input verifies: 1 <= (numerator + delta) / denominator < 10.
  270. static void BignumToFixed(
  271. int requested_digits,
  272. ref int decimal_point,
  273. Bignum numerator,
  274. Bignum denominator,
  275. ref DtoaBuilder buffer)
  276. {
  277. // Note that we have to look at more than just the requested_digits, since
  278. // a number could be rounded up. Example: v=0.5 with requested_digits=0.
  279. // Even though the power of v equals 0 we can't just stop here.
  280. if (-(decimal_point) > requested_digits)
  281. {
  282. // The number is definitively too small.
  283. // Ex: 0.001 with requested_digits == 1.
  284. // Set decimal-point to -requested_digits. This is what Gay does.
  285. // Note that it should not have any effect anyways since the string is
  286. // empty.
  287. decimal_point = -requested_digits;
  288. buffer.Reset();
  289. return;
  290. }
  291. if (-decimal_point == requested_digits)
  292. {
  293. // We only need to verify if the number rounds down or up.
  294. // Ex: 0.04 and 0.06 with requested_digits == 1.
  295. Debug.Assert(decimal_point == -requested_digits);
  296. // Initially the fraction lies in range (1, 10]. Multiply the denominator
  297. // by 10 so that we can compare more easily.
  298. denominator.Times10();
  299. if (Bignum.PlusCompare(numerator, numerator, denominator) >= 0)
  300. {
  301. // If the fraction is >= 0.5 then we have to include the rounded
  302. // digit.
  303. buffer[0] = '1';
  304. decimal_point++;
  305. }
  306. else
  307. {
  308. // Note that we caught most of similar cases earlier.
  309. buffer.Reset();
  310. }
  311. }
  312. else
  313. {
  314. // The requested digits correspond to the digits after the point.
  315. // The variable 'needed_digits' includes the digits before the point.
  316. int needed_digits = (decimal_point) + requested_digits;
  317. GenerateCountedDigits(needed_digits, ref decimal_point, numerator, denominator, ref buffer);
  318. }
  319. }
  320. // Returns an estimation of k such that 10^(k-1) <= v < 10^k where
  321. // v = f * 2^exponent and 2^52 <= f < 2^53.
  322. // v is hence a normalized double with the given exponent. The output is an
  323. // approximation for the exponent of the decimal approimation .digits * 10^k.
  324. //
  325. // The result might undershoot by 1 in which case 10^k <= v < 10^k+1.
  326. // Note: this property holds for v's upper boundary m+ too.
  327. // 10^k <= m+ < 10^k+1.
  328. // (see explanation below).
  329. //
  330. // Examples:
  331. // EstimatePower(0) => 16
  332. // EstimatePower(-52) => 0
  333. //
  334. // Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0.
  335. private static int EstimatePower(int exponent)
  336. {
  337. // This function estimates log10 of v where v = f*2^e (with e == exponent).
  338. // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)).
  339. // Note that f is bounded by its container size. Let p = 53 (the double's
  340. // significand size). Then 2^(p-1) <= f < 2^p.
  341. //
  342. // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close
  343. // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)).
  344. // The computed number undershoots by less than 0.631 (when we compute log3
  345. // and not log10).
  346. //
  347. // Optimization: since we only need an approximated result this computation
  348. // can be performed on 64 bit integers. On x86/x64 architecture the speedup is
  349. // not really measurable, though.
  350. //
  351. // Since we want to avoid overshooting we decrement by 1e10 so that
  352. // floating-point imprecisions don't affect us.
  353. //
  354. // Explanation for v's boundary m+: the computation takes advantage of
  355. // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement
  356. // (even for denormals where the delta can be much more important).
  357. const double k1Log10 = 0.30102999566398114; // 1/lg(10)
  358. // For doubles len(f) == 53 (don't forget the hidden bit).
  359. const int kSignificandSize = 53;
  360. double estimate = System.Math.Ceiling((exponent + kSignificandSize - 1) * k1Log10 - 1e-10);
  361. return (int) estimate;
  362. }
  363. // See comments for InitialScaledStartValues.
  364. private static void InitialScaledStartValuesPositiveExponent(
  365. double v,
  366. int estimated_power,
  367. bool need_boundary_deltas,
  368. Bignum numerator,
  369. Bignum denominator,
  370. Bignum delta_minus,
  371. Bignum delta_plus)
  372. {
  373. // A positive exponent implies a positive power.
  374. Debug.Assert(estimated_power >= 0);
  375. // Since the estimated_power is positive we simply multiply the denominator
  376. // by 10^estimated_power.
  377. // numerator = v.
  378. var bits = (ulong) BitConverter.DoubleToInt64Bits(v);
  379. numerator.AssignUInt64(DoubleHelper.Significand(bits));
  380. numerator.ShiftLeft(DoubleHelper.Exponent(bits));
  381. // denominator = 10^estimated_power.
  382. denominator.AssignPowerUInt16(10, estimated_power);
  383. if (need_boundary_deltas)
  384. {
  385. // Introduce a common denominator so that the deltas to the boundaries are
  386. // integers.
  387. denominator.ShiftLeft(1);
  388. numerator.ShiftLeft(1);
  389. // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
  390. // denominator (of 2) delta_plus equals 2^e.
  391. delta_plus.AssignUInt16(1);
  392. delta_plus.ShiftLeft(DoubleHelper.Exponent(bits));
  393. // Same for delta_minus (with adjustments below if f == 2^p-1).
  394. delta_minus.AssignUInt16(1);
  395. delta_minus.ShiftLeft(DoubleHelper.Exponent(bits));
  396. // If the significand (without the hidden bit) is 0, then the lower
  397. // boundary is closer than just half a ulp (unit in the last place).
  398. // There is only one exception: if the next lower number is a denormal then
  399. // the distance is 1 ulp. This cannot be the case for exponent >= 0 (but we
  400. // have to test it in the other function where exponent < 0).
  401. ulong v_bits = bits;
  402. if ((v_bits & DoubleHelper.KSignificandMask) == 0)
  403. {
  404. // The lower boundary is closer at half the distance of "normal" numbers.
  405. // Increase the common denominator and adapt all but the delta_minus.
  406. denominator.ShiftLeft(1); // *2
  407. numerator.ShiftLeft(1); // *2
  408. delta_plus.ShiftLeft(1); // *2
  409. }
  410. }
  411. }
  412. // See comments for InitialScaledStartValues
  413. private static void InitialScaledStartValuesNegativeExponentPositivePower(
  414. double v,
  415. int estimated_power,
  416. bool need_boundary_deltas,
  417. Bignum numerator,
  418. Bignum denominator,
  419. Bignum delta_minus,
  420. Bignum delta_plus)
  421. {
  422. var bits = (ulong) BitConverter.DoubleToInt64Bits(v);
  423. ulong significand = DoubleHelper.Significand(bits);
  424. int exponent = DoubleHelper.Exponent(bits);
  425. // v = f * 2^e with e < 0, and with estimated_power >= 0.
  426. // This means that e is close to 0 (have a look at how estimated_power is
  427. // computed).
  428. // numerator = significand
  429. // since v = significand * 2^exponent this is equivalent to
  430. // numerator = v * / 2^-exponent
  431. numerator.AssignUInt64(significand);
  432. // denominator = 10^estimated_power * 2^-exponent (with exponent < 0)
  433. denominator.AssignPowerUInt16(10, estimated_power);
  434. denominator.ShiftLeft(-exponent);
  435. if (need_boundary_deltas)
  436. {
  437. // Introduce a common denominator so that the deltas to the boundaries are
  438. // integers.
  439. denominator.ShiftLeft(1);
  440. numerator.ShiftLeft(1);
  441. // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
  442. // denominator (of 2) delta_plus equals 2^e.
  443. // Given that the denominator already includes v's exponent the distance
  444. // to the boundaries is simply 1.
  445. delta_plus.AssignUInt16(1);
  446. // Same for delta_minus (with adjustments below if f == 2^p-1).
  447. delta_minus.AssignUInt16(1);
  448. // If the significand (without the hidden bit) is 0, then the lower
  449. // boundary is closer than just one ulp (unit in the last place).
  450. // There is only one exception: if the next lower number is a denormal
  451. // then the distance is 1 ulp. Since the exponent is close to zero
  452. // (otherwise estimated_power would have been negative) this cannot happen
  453. // here either.
  454. ulong v_bits = bits;
  455. if ((v_bits & DoubleHelper.KSignificandMask) == 0)
  456. {
  457. // The lower boundary is closer at half the distance of "normal" numbers.
  458. // Increase the denominator and adapt all but the delta_minus.
  459. denominator.ShiftLeft(1); // *2
  460. numerator.ShiftLeft(1); // *2
  461. delta_plus.ShiftLeft(1); // *2
  462. }
  463. }
  464. }
  465. // See comments for InitialScaledStartValues
  466. private static void InitialScaledStartValuesNegativeExponentNegativePower(
  467. double v,
  468. int estimated_power,
  469. bool need_boundary_deltas,
  470. Bignum numerator,
  471. Bignum denominator,
  472. Bignum delta_minus,
  473. Bignum delta_plus)
  474. {
  475. const ulong kMinimalNormalizedExponent = 0x0010000000000000;
  476. var bits = (ulong) BitConverter.DoubleToInt64Bits(v);
  477. ulong significand = DoubleHelper.Significand(bits);
  478. int exponent = DoubleHelper.Exponent(bits);
  479. // Instead of multiplying the denominator with 10^estimated_power we
  480. // multiply all values (numerator and deltas) by 10^-estimated_power.
  481. // Use numerator as temporary container for power_ten.
  482. Bignum power_ten = numerator;
  483. power_ten.AssignPowerUInt16(10, -estimated_power);
  484. if (need_boundary_deltas)
  485. {
  486. // Since power_ten == numerator we must make a copy of 10^estimated_power
  487. // before we complete the computation of the numerator.
  488. // delta_plus = delta_minus = 10^estimated_power
  489. delta_plus.AssignBignum(power_ten);
  490. delta_minus.AssignBignum(power_ten);
  491. }
  492. // numerator = significand * 2 * 10^-estimated_power
  493. // since v = significand * 2^exponent this is equivalent to
  494. // numerator = v * 10^-estimated_power * 2 * 2^-exponent.
  495. // Remember: numerator has been abused as power_ten. So no need to assign it
  496. // to itself.
  497. Debug.Assert(numerator == power_ten);
  498. numerator.MultiplyByUInt64(significand);
  499. // denominator = 2 * 2^-exponent with exponent < 0.
  500. denominator.AssignUInt16(1);
  501. denominator.ShiftLeft(-exponent);
  502. if (need_boundary_deltas)
  503. {
  504. // Introduce a common denominator so that the deltas to the boundaries are
  505. // integers.
  506. numerator.ShiftLeft(1);
  507. denominator.ShiftLeft(1);
  508. // With this shift the boundaries have their correct value, since
  509. // delta_plus = 10^-estimated_power, and
  510. // delta_minus = 10^-estimated_power.
  511. // These assignments have been done earlier.
  512. // The special case where the lower boundary is twice as close.
  513. // This time we have to look out for the exception too.
  514. ulong v_bits = bits;
  515. if ((v_bits & DoubleHelper.KSignificandMask) == 0 &&
  516. // The only exception where a significand == 0 has its boundaries at
  517. // "normal" distances:
  518. (v_bits & DoubleHelper.KExponentMask) != kMinimalNormalizedExponent)
  519. {
  520. numerator.ShiftLeft(1); // *2
  521. denominator.ShiftLeft(1); // *2
  522. delta_plus.ShiftLeft(1); // *2
  523. }
  524. }
  525. }
  526. // Let v = significand * 2^exponent.
  527. // Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
  528. // and denominator. The functions GenerateShortestDigits and
  529. // GenerateCountedDigits will then convert this ratio to its decimal
  530. // representation d, with the required accuracy.
  531. // Then d * 10^estimated_power is the representation of v.
  532. // (Note: the fraction and the estimated_power might get adjusted before
  533. // generating the decimal representation.)
  534. //
  535. // The initial start values consist of:
  536. // - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power.
  537. // - a scaled (common) denominator.
  538. // optionally (used by GenerateShortestDigits to decide if it has the shortest
  539. // decimal converting back to v):
  540. // - v - m-: the distance to the lower boundary.
  541. // - m+ - v: the distance to the upper boundary.
  542. //
  543. // v, m+, m-, and therefore v - m- and m+ - v all share the same denominator.
  544. //
  545. // Let ep == estimated_power, then the returned values will satisfy:
  546. // v / 10^ep = numerator / denominator.
  547. // v's boundarys m- and m+:
  548. // m- / 10^ep == v / 10^ep - delta_minus / denominator
  549. // m+ / 10^ep == v / 10^ep + delta_plus / denominator
  550. // Or in other words:
  551. // m- == v - delta_minus * 10^ep / denominator;
  552. // m+ == v + delta_plus * 10^ep / denominator;
  553. //
  554. // Since 10^(k-1) <= v < 10^k (with k == estimated_power)
  555. // or 10^k <= v < 10^(k+1)
  556. // we then have 0.1 <= numerator/denominator < 1
  557. // or 1 <= numerator/denominator < 10
  558. //
  559. // It is then easy to kickstart the digit-generation routine.
  560. //
  561. // The boundary-deltas are only filled if need_boundary_deltas is set.
  562. private static void InitialScaledStartValues(
  563. double v,
  564. int estimated_power,
  565. bool need_boundary_deltas,
  566. Bignum numerator,
  567. Bignum denominator,
  568. Bignum delta_minus,
  569. Bignum delta_plus)
  570. {
  571. var bits = (ulong) BitConverter.DoubleToInt64Bits(v);
  572. if (DoubleHelper.Exponent(bits) >= 0)
  573. {
  574. InitialScaledStartValuesPositiveExponent(
  575. v,
  576. estimated_power,
  577. need_boundary_deltas,
  578. numerator,
  579. denominator,
  580. delta_minus,
  581. delta_plus);
  582. }
  583. else if (estimated_power >= 0)
  584. {
  585. InitialScaledStartValuesNegativeExponentPositivePower(
  586. v,
  587. estimated_power,
  588. need_boundary_deltas,
  589. numerator,
  590. denominator,
  591. delta_minus,
  592. delta_plus);
  593. }
  594. else
  595. {
  596. InitialScaledStartValuesNegativeExponentNegativePower(
  597. v,
  598. estimated_power,
  599. need_boundary_deltas,
  600. numerator,
  601. denominator,
  602. delta_minus,
  603. delta_plus);
  604. }
  605. }
  606. // This routine multiplies numerator/denominator so that its values lies in the
  607. // range 1-10. That is after a call to this function we have:
  608. // 1 <= (numerator + delta_plus) /denominator < 10.
  609. // Let numerator the input before modification and numerator' the argument
  610. // after modification, then the output-parameter decimal_point is such that
  611. // numerator / denominator * 10^estimated_power ==
  612. // numerator' / denominator' * 10^(decimal_point - 1)
  613. // In some cases estimated_power was too low, and this is already the case. We
  614. // then simply adjust the power so that 10^(k-1) <= v < 10^k (with k ==
  615. // estimated_power) but do not touch the numerator or denominator.
  616. // Otherwise the routine multiplies the numerator and the deltas by 10.
  617. private static void FixupMultiply10(
  618. int estimated_power,
  619. bool is_even,
  620. out int decimal_point,
  621. Bignum numerator,
  622. Bignum denominator,
  623. Bignum delta_minus,
  624. Bignum delta_plus)
  625. {
  626. bool in_range;
  627. if (is_even)
  628. in_range = Bignum.PlusCompare(numerator, delta_plus, denominator) >= 0;
  629. else
  630. in_range = Bignum.PlusCompare(numerator, delta_plus, denominator) > 0;
  631. if (in_range)
  632. {
  633. // Since numerator + delta_plus >= denominator we already have
  634. // 1 <= numerator/denominator < 10. Simply update the estimated_power.
  635. decimal_point = estimated_power + 1;
  636. }
  637. else
  638. {
  639. decimal_point = estimated_power;
  640. numerator.Times10();
  641. if (Bignum.Equal(delta_minus, delta_plus))
  642. {
  643. delta_minus.Times10();
  644. delta_plus.AssignBignum(delta_minus);
  645. }
  646. else
  647. {
  648. delta_minus.Times10();
  649. delta_plus.Times10();
  650. }
  651. }
  652. }
  653. }
  654. }