BignumDtoa.cs 28 KB

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