/Libraries/BigRational/BigRationalLibrary/BigRational.cs

# · C# · 822 lines · 581 code · 126 blank · 115 comment · 122 complexity · 5beb8f07c2d1d80dbcc3505a2534db59 MD5 · raw file

  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. /*============================================================
  3. ** Class: BigRational
  4. **
  5. ** Purpose:
  6. ** --------
  7. ** This class is used to represent an arbitrary precision
  8. ** BigRational number
  9. **
  10. ** A rational number (commonly called a fraction) is a ratio
  11. ** between two integers. For example (3/6) = (2/4) = (1/2)
  12. **
  13. ** Arithmetic
  14. ** ----------
  15. ** a/b = c/d, iff ad = bc
  16. ** a/b + c/d == (ad + bc)/bd
  17. ** a/b - c/d == (ad - bc)/bd
  18. ** a/b % c/d == (ad % bc)/bd
  19. ** a/b * c/d == (ac)/(bd)
  20. ** a/b / c/d == (ad)/(bc)
  21. ** -(a/b) == (-a)/b
  22. ** (a/b)^(-1) == b/a, if a != 0
  23. **
  24. ** Reduction Algorithm
  25. ** ------------------------
  26. ** Euclid's algorithm is used to simplify the fraction.
  27. ** Calculating the greatest common divisor of two n-digit
  28. ** numbers can be found in
  29. **
  30. ** O(n(log n)^5 (log log n)) steps as n -> +infinity
  31. ============================================================*/
  32. namespace Numerics {
  33. using System;
  34. using System.Collections.Generic;
  35. using System.Diagnostics;
  36. using System.Globalization;
  37. using System.Numerics;
  38. using System.Runtime.InteropServices;
  39. using System.Runtime.Serialization;
  40. using System.Security.Permissions;
  41. using System.Text;
  42. [Serializable]
  43. [ComVisible(false)]
  44. public struct BigRational : IComparable, IComparable<BigRational>, IDeserializationCallback, IEquatable<BigRational>, ISerializable {
  45. // ---- SECTION: members supporting exposed properties -------------*
  46. private BigInteger m_numerator;
  47. private BigInteger m_denominator;
  48. private static readonly BigRational s_brZero = new BigRational(BigInteger.Zero);
  49. private static readonly BigRational s_brOne = new BigRational(BigInteger.One);
  50. private static readonly BigRational s_brMinusOne = new BigRational(BigInteger.MinusOne);
  51. // ---- SECTION: members for internal support ---------*
  52. #region Members for Internal Support
  53. [StructLayout(LayoutKind.Explicit)]
  54. internal struct DoubleUlong {
  55. [FieldOffset(0)]
  56. public double dbl;
  57. [FieldOffset(0)]
  58. public ulong uu;
  59. }
  60. private const int DoubleMaxScale = 308;
  61. private static readonly BigInteger s_bnDoublePrecision = BigInteger.Pow(10, DoubleMaxScale);
  62. private static readonly BigInteger s_bnDoubleMaxValue = (BigInteger) Double.MaxValue;
  63. private static readonly BigInteger s_bnDoubleMinValue = (BigInteger) Double.MinValue;
  64. [StructLayout(LayoutKind.Explicit)]
  65. internal struct DecimalUInt32 {
  66. [FieldOffset(0)]
  67. public Decimal dec;
  68. [FieldOffset(0)]
  69. public int flags;
  70. }
  71. private const int DecimalScaleMask = 0x00FF0000;
  72. private const int DecimalSignMask = unchecked((int)0x80000000);
  73. private const int DecimalMaxScale = 28;
  74. private static readonly BigInteger s_bnDecimalPrecision = BigInteger.Pow(10, DecimalMaxScale);
  75. private static readonly BigInteger s_bnDecimalMaxValue = (BigInteger) Decimal.MaxValue;
  76. private static readonly BigInteger s_bnDecimalMinValue = (BigInteger) Decimal.MinValue;
  77. private const String c_solidus = @"/";
  78. #endregion Members for Internal Support
  79. // ---- SECTION: public properties --------------*
  80. #region Public Properties
  81. public static BigRational Zero {
  82. get {
  83. return s_brZero;
  84. }
  85. }
  86. public static BigRational One {
  87. get {
  88. return s_brOne;
  89. }
  90. }
  91. public static BigRational MinusOne {
  92. get {
  93. return s_brMinusOne;
  94. }
  95. }
  96. public Int32 Sign {
  97. get {
  98. return m_numerator.Sign;
  99. }
  100. }
  101. public BigInteger Numerator {
  102. get {
  103. return m_numerator;
  104. }
  105. }
  106. public BigInteger Denominator {
  107. get {
  108. return m_denominator;
  109. }
  110. }
  111. #endregion Public Properties
  112. // ---- SECTION: public instance methods --------------*
  113. #region Public Instance Methods
  114. // GetWholePart() and GetFractionPart()
  115. //
  116. // BigRational == Whole, Fraction
  117. // 0/2 == 0, 0/2
  118. // 1/2 == 0, 1/2
  119. // -1/2 == 0, -1/2
  120. // 1/1 == 1, 0/1
  121. // -1/1 == -1, 0/1
  122. // -3/2 == -1, -1/2
  123. // 3/2 == 1, 1/2
  124. public BigInteger GetWholePart() {
  125. return BigInteger.Divide(m_numerator, m_denominator);
  126. }
  127. public BigRational GetFractionPart() {
  128. return new BigRational(BigInteger.Remainder(m_numerator, m_denominator), m_denominator);
  129. }
  130. public override bool Equals(Object obj) {
  131. if (obj == null)
  132. return false;
  133. if (!(obj is BigRational))
  134. return false;
  135. return this.Equals((BigRational)obj);
  136. }
  137. public override int GetHashCode() {
  138. return (m_numerator / Denominator).GetHashCode();
  139. }
  140. // IComparable
  141. int IComparable.CompareTo(Object obj) {
  142. if (obj == null)
  143. return 1;
  144. if (!(obj is BigRational))
  145. throw new ArgumentException("Argument must be of type BigRational", "obj");
  146. return Compare(this, (BigRational)obj);
  147. }
  148. // IComparable<BigRational>
  149. public int CompareTo(BigRational other) {
  150. return Compare(this, other);
  151. }
  152. // Object.ToString
  153. public override String ToString() {
  154. StringBuilder ret = new StringBuilder();
  155. ret.Append(m_numerator.ToString("R", CultureInfo.InvariantCulture));
  156. ret.Append(c_solidus);
  157. ret.Append(Denominator.ToString("R", CultureInfo.InvariantCulture));
  158. return ret.ToString();
  159. }
  160. // IEquatable<BigRational>
  161. // a/b = c/d, iff ad = bc
  162. public Boolean Equals(BigRational other) {
  163. if (this.Denominator == other.Denominator) {
  164. return m_numerator == other.m_numerator;
  165. }
  166. else {
  167. return (m_numerator * other.Denominator) == (Denominator * other.m_numerator);
  168. }
  169. }
  170. #endregion Public Instance Methods
  171. // -------- SECTION: constructors -----------------*
  172. #region Constructors
  173. public BigRational(BigInteger numerator) {
  174. m_numerator = numerator;
  175. m_denominator = BigInteger.One;
  176. }
  177. // BigRational(Double)
  178. public BigRational(Double value) {
  179. if (Double.IsNaN(value)) {
  180. throw new ArgumentException("Argument is not a number", "value");
  181. }
  182. else if (Double.IsInfinity(value)) {
  183. throw new ArgumentException("Argument is infinity", "value");
  184. }
  185. bool isFinite;
  186. int sign;
  187. int exponent;
  188. ulong significand;
  189. SplitDoubleIntoParts(value, out sign, out exponent, out significand, out isFinite);
  190. if (significand == 0) {
  191. this = BigRational.Zero;
  192. return;
  193. }
  194. m_numerator = significand;
  195. m_denominator = 1 << 52;
  196. if (exponent > 0) {
  197. m_numerator = BigInteger.Pow(m_numerator, exponent);
  198. }
  199. else if (exponent < 0) {
  200. m_denominator = BigInteger.Pow(m_denominator, -exponent);
  201. }
  202. if (sign < 0) {
  203. m_numerator = BigInteger.Negate(m_numerator);
  204. }
  205. Simplify();
  206. }
  207. // BigRational(Decimal) -
  208. //
  209. // The Decimal type represents floating point numbers exactly, with no rounding error.
  210. // Values such as "0.1" in Decimal are actually representable, and convert cleanly
  211. // to BigRational as "11/10"
  212. public BigRational(Decimal value) {
  213. int[] bits = Decimal.GetBits(value);
  214. if (bits == null || bits.Length != 4 || (bits[3] & ~(DecimalSignMask | DecimalScaleMask)) != 0 || (bits[3] & DecimalScaleMask) > (28 << 16)) {
  215. throw new ArgumentException("invalid Decimal", "value");
  216. }
  217. if (value == Decimal.Zero) {
  218. this = BigRational.Zero;
  219. return;
  220. }
  221. // build up the numerator
  222. ulong ul = (((ulong)(uint)bits[2]) << 32) | ((ulong)(uint)bits[1]); // (hi << 32) | (mid)
  223. m_numerator = (new BigInteger(ul) << 32) | (uint)bits[0]; // (hiMid << 32) | (low)
  224. bool isNegative = (bits[3] & DecimalSignMask) != 0;
  225. if (isNegative) {
  226. m_numerator = BigInteger.Negate(m_numerator);
  227. }
  228. // build up the denominator
  229. int scale = (bits[3] & DecimalScaleMask) >> 16; // 0-28, power of 10 to divide numerator by
  230. m_denominator = BigInteger.Pow(10, scale);
  231. Simplify();
  232. }
  233. public BigRational(BigInteger numerator, BigInteger denominator) {
  234. if (denominator.Sign == 0) {
  235. throw new DivideByZeroException();
  236. }
  237. else if (numerator.Sign == 0) {
  238. // 0/m -> 0/1
  239. m_numerator = BigInteger.Zero;
  240. m_denominator = BigInteger.One;
  241. }
  242. else if (denominator.Sign < 0) {
  243. m_numerator = BigInteger.Negate(numerator);
  244. m_denominator = BigInteger.Negate(denominator);
  245. }
  246. else {
  247. m_numerator = numerator;
  248. m_denominator = denominator;
  249. }
  250. Simplify();
  251. }
  252. public BigRational(BigInteger whole, BigInteger numerator, BigInteger denominator) {
  253. if (denominator.Sign == 0) {
  254. throw new DivideByZeroException();
  255. }
  256. else if (numerator.Sign == 0 && whole.Sign == 0) {
  257. m_numerator = BigInteger.Zero;
  258. m_denominator = BigInteger.One;
  259. }
  260. else if (denominator.Sign < 0) {
  261. m_denominator = BigInteger.Negate(denominator);
  262. m_numerator = (BigInteger.Negate(whole) * m_denominator) + BigInteger.Negate(numerator);
  263. }
  264. else {
  265. m_denominator = denominator;
  266. m_numerator = (whole * denominator) + numerator;
  267. }
  268. Simplify();
  269. }
  270. #endregion Constructors
  271. // -------- SECTION: public static methods -----------------*
  272. #region Public Static Methods
  273. public static BigRational Abs(BigRational r) {
  274. return (r.m_numerator.Sign < 0 ? new BigRational(BigInteger.Abs(r.m_numerator), r.Denominator) : r);
  275. }
  276. public static BigRational Negate(BigRational r) {
  277. return new BigRational(BigInteger.Negate(r.m_numerator), r.Denominator);
  278. }
  279. public static BigRational Invert(BigRational r) {
  280. return new BigRational(r.Denominator, r.m_numerator);
  281. }
  282. public static BigRational Add(BigRational x, BigRational y) {
  283. return x + y;
  284. }
  285. public static BigRational Subtract(BigRational x, BigRational y) {
  286. return x - y;
  287. }
  288. public static BigRational Multiply(BigRational x, BigRational y) {
  289. return x * y;
  290. }
  291. public static BigRational Divide(BigRational dividend, BigRational divisor) {
  292. return dividend / divisor;
  293. }
  294. public static BigRational Remainder(BigRational dividend, BigRational divisor) {
  295. return dividend % divisor;
  296. }
  297. public static BigRational DivRem(BigRational dividend, BigRational divisor, out BigRational remainder) {
  298. // a/b / c/d == (ad)/(bc)
  299. // a/b % c/d == (ad % bc)/bd
  300. // (ad) and (bc) need to be calculated for both the division and the remainder operations.
  301. BigInteger ad = dividend.m_numerator * divisor.Denominator;
  302. BigInteger bc = dividend.Denominator * divisor.m_numerator;
  303. BigInteger bd = dividend.Denominator * divisor.Denominator;
  304. remainder = new BigRational(ad % bc, bd);
  305. return new BigRational(ad, bc);
  306. }
  307. public static BigRational Pow(BigRational baseValue, BigInteger exponent) {
  308. if (exponent.Sign == 0) {
  309. // 0^0 -> 1
  310. // n^0 -> 1
  311. return BigRational.One;
  312. }
  313. else if (exponent.Sign < 0) {
  314. if (baseValue == BigRational.Zero) {
  315. throw new ArgumentException("cannot raise zero to a negative power", "baseValue");
  316. }
  317. // n^(-e) -> (1/n)^e
  318. baseValue = BigRational.Invert(baseValue);
  319. exponent = BigInteger.Negate(exponent);
  320. }
  321. BigRational result = baseValue;
  322. while (exponent > BigInteger.One) {
  323. result = result * baseValue;
  324. exponent--;
  325. }
  326. return result;
  327. }
  328. // Least Common Denominator (LCD)
  329. //
  330. // The LCD is the least common multiple of the two denominators. For instance, the LCD of
  331. // {1/2, 1/4} is 4 because the least common multiple of 2 and 4 is 4. Likewise, the LCD
  332. // of {1/2, 1/3} is 6.
  333. //
  334. // To find the LCD:
  335. //
  336. // 1) Find the Greatest Common Divisor (GCD) of the denominators
  337. // 2) Multiply the denominators together
  338. // 3) Divide the product of the denominators by the GCD
  339. public static BigInteger LeastCommonDenominator(BigRational x, BigRational y) {
  340. // LCD( a/b, c/d ) == (bd) / gcd(b,d)
  341. return (x.Denominator * y.Denominator) / BigInteger.GreatestCommonDivisor(x.Denominator, y.Denominator);
  342. }
  343. public static int Compare(BigRational r1, BigRational r2) {
  344. // a/b = c/d, iff ad = bc
  345. return BigInteger.Compare(r1.m_numerator * r2.Denominator, r2.m_numerator * r1.Denominator);
  346. }
  347. #endregion Public Static Methods
  348. #region Operator Overloads
  349. public static bool operator ==(BigRational x, BigRational y) {
  350. return Compare(x, y) == 0;
  351. }
  352. public static bool operator !=(BigRational x, BigRational y) {
  353. return Compare(x, y) != 0;
  354. }
  355. public static bool operator <(BigRational x, BigRational y) {
  356. return Compare(x, y) < 0;
  357. }
  358. public static bool operator <=(BigRational x, BigRational y) {
  359. return Compare(x, y) <= 0;
  360. }
  361. public static bool operator >(BigRational x, BigRational y) {
  362. return Compare(x, y) > 0;
  363. }
  364. public static bool operator >=(BigRational x, BigRational y) {
  365. return Compare(x, y) >= 0;
  366. }
  367. public static BigRational operator +(BigRational r) {
  368. return r;
  369. }
  370. public static BigRational operator -(BigRational r) {
  371. return new BigRational(-r.m_numerator, r.Denominator);
  372. }
  373. public static BigRational operator ++ (BigRational r) {
  374. return r + BigRational.One;
  375. }
  376. public static BigRational operator -- (BigRational r) {
  377. return r - BigRational.One;
  378. }
  379. public static BigRational operator +(BigRational r1, BigRational r2) {
  380. // a/b + c/d == (ad + bc)/bd
  381. return new BigRational((r1.m_numerator * r2.Denominator) + (r1.Denominator * r2.m_numerator), (r1.Denominator * r2.Denominator));
  382. }
  383. public static BigRational operator -(BigRational r1, BigRational r2) {
  384. // a/b - c/d == (ad - bc)/bd
  385. return new BigRational((r1.m_numerator * r2.Denominator) - (r1.Denominator * r2.m_numerator), (r1.Denominator * r2.Denominator));
  386. }
  387. public static BigRational operator *(BigRational r1, BigRational r2) {
  388. // a/b * c/d == (ac)/(bd)
  389. return new BigRational((r1.m_numerator * r2.m_numerator), (r1.Denominator * r2.Denominator));
  390. }
  391. public static BigRational operator /(BigRational r1, BigRational r2) {
  392. // a/b / c/d == (ad)/(bc)
  393. return new BigRational((r1.m_numerator * r2.Denominator), (r1.Denominator * r2.m_numerator));
  394. }
  395. public static BigRational operator %(BigRational r1, BigRational r2) {
  396. // a/b % c/d == (ad % bc)/bd
  397. return new BigRational((r1.m_numerator * r2.Denominator) % (r1.Denominator * r2.m_numerator), (r1.Denominator * r2.Denominator));
  398. }
  399. #endregion Operator Overloads
  400. // ----- SECTION: explicit conversions from BigRational to numeric base types ----------------*
  401. #region explicit conversions from BigRational
  402. [CLSCompliant(false)]
  403. public static explicit operator SByte(BigRational value) {
  404. return (SByte)(BigInteger.Divide(value.m_numerator, value.m_denominator));
  405. }
  406. [CLSCompliant(false)]
  407. public static explicit operator UInt16(BigRational value) {
  408. return (UInt16)(BigInteger.Divide(value.m_numerator, value.m_denominator));
  409. }
  410. [CLSCompliant(false)]
  411. public static explicit operator UInt32(BigRational value) {
  412. return (UInt32)(BigInteger.Divide(value.m_numerator, value.m_denominator));
  413. }
  414. [CLSCompliant(false)]
  415. public static explicit operator UInt64(BigRational value) {
  416. return (UInt64)(BigInteger.Divide(value.m_numerator, value.m_denominator));
  417. }
  418. public static explicit operator Byte(BigRational value) {
  419. return (Byte)(BigInteger.Divide(value.m_numerator, value.m_denominator));
  420. }
  421. public static explicit operator Int16(BigRational value) {
  422. return (Int16)(BigInteger.Divide(value.m_numerator, value.m_denominator));
  423. }
  424. public static explicit operator Int32(BigRational value) {
  425. return (Int32)(BigInteger.Divide(value.m_numerator, value.m_denominator));
  426. }
  427. public static explicit operator Int64(BigRational value) {
  428. return (Int64)(BigInteger.Divide(value.m_numerator, value.m_denominator));
  429. }
  430. public static explicit operator BigInteger(BigRational value) {
  431. return BigInteger.Divide(value.m_numerator, value.m_denominator);
  432. }
  433. public static explicit operator Single(BigRational value) {
  434. // The Single value type represents a single-precision 32-bit number with
  435. // values ranging from negative 3.402823e38 to positive 3.402823e38
  436. // values that do not fit into this range are returned as Infinity
  437. return (Single)((Double)value);
  438. }
  439. public static explicit operator Double(BigRational value) {
  440. // The Double value type represents a double-precision 64-bit number with
  441. // values ranging from -1.79769313486232e308 to +1.79769313486232e308
  442. // values that do not fit into this range are returned as +/-Infinity
  443. if (SafeCastToDouble(value.m_numerator) && SafeCastToDouble(value.m_denominator)) {
  444. return (Double) value.m_numerator / (Double) value.m_denominator;
  445. }
  446. // scale the numerator to preseve the fraction part through the integer division
  447. BigInteger denormalized = (value.m_numerator * s_bnDoublePrecision) / value.m_denominator;
  448. if (denormalized.IsZero)
  449. return (value.Sign < 0) ? BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000)) : 0d; // underflow to -+0
  450. Double result = 0;
  451. bool isDouble = false;
  452. int scale = DoubleMaxScale;
  453. while (scale > 0) {
  454. if (!isDouble) {
  455. if (SafeCastToDouble(denormalized)) {
  456. result = (Double) denormalized;
  457. isDouble = true;
  458. }
  459. else {
  460. denormalized = denormalized / 10;
  461. }
  462. }
  463. result = result / 10;
  464. scale--;
  465. }
  466. if (!isDouble)
  467. return (value.Sign < 0) ? Double.NegativeInfinity : Double.PositiveInfinity;
  468. else
  469. return result;
  470. }
  471. public static explicit operator Decimal(BigRational value) {
  472. // The Decimal value type represents decimal numbers ranging
  473. // from +79,228,162,514,264,337,593,543,950,335 to -79,228,162,514,264,337,593,543,950,335
  474. // the binary representation of a Decimal value is of the form, ((-2^96 to 2^96) / 10^(0 to 28))
  475. if (SafeCastToDecimal(value.m_numerator) && SafeCastToDecimal(value.m_denominator)) {
  476. return (Decimal) value.m_numerator / (Decimal) value.m_denominator;
  477. }
  478. // scale the numerator to preseve the fraction part through the integer division
  479. BigInteger denormalized = (value.m_numerator * s_bnDecimalPrecision) / value.m_denominator;
  480. if (denormalized.IsZero) {
  481. return Decimal.Zero; // underflow - fraction is too small to fit in a decimal
  482. }
  483. for (int scale = DecimalMaxScale; scale >= 0; scale--) {
  484. if (!SafeCastToDecimal(denormalized)) {
  485. denormalized = denormalized / 10;
  486. }
  487. else {
  488. DecimalUInt32 dec = new DecimalUInt32();
  489. dec.dec = (Decimal) denormalized;
  490. dec.flags = (dec.flags & ~DecimalScaleMask) | (scale << 16);
  491. return dec.dec;
  492. }
  493. }
  494. throw new OverflowException("Value was either too large or too small for a Decimal.");
  495. }
  496. #endregion explicit conversions from BigRational
  497. // ----- SECTION: implicit conversions from numeric base types to BigRational ----------------*
  498. #region implicit conversions to BigRational
  499. [CLSCompliant(false)]
  500. public static implicit operator BigRational(SByte value) {
  501. return new BigRational((BigInteger)value);
  502. }
  503. [CLSCompliant(false)]
  504. public static implicit operator BigRational(UInt16 value) {
  505. return new BigRational((BigInteger)value);
  506. }
  507. [CLSCompliant(false)]
  508. public static implicit operator BigRational(UInt32 value) {
  509. return new BigRational((BigInteger)value);
  510. }
  511. [CLSCompliant(false)]
  512. public static implicit operator BigRational(UInt64 value) {
  513. return new BigRational((BigInteger)value);
  514. }
  515. public static implicit operator BigRational(Byte value) {
  516. return new BigRational((BigInteger)value);
  517. }
  518. public static implicit operator BigRational(Int16 value) {
  519. return new BigRational((BigInteger)value);
  520. }
  521. public static implicit operator BigRational(Int32 value) {
  522. return new BigRational((BigInteger)value);
  523. }
  524. public static implicit operator BigRational(Int64 value) {
  525. return new BigRational((BigInteger)value);
  526. }
  527. public static implicit operator BigRational(BigInteger value) {
  528. return new BigRational(value);
  529. }
  530. public static implicit operator BigRational(Single value) {
  531. return new BigRational((Double)value);
  532. }
  533. public static implicit operator BigRational(Double value) {
  534. return new BigRational(value);
  535. }
  536. public static implicit operator BigRational(Decimal value) {
  537. return new BigRational(value);
  538. }
  539. #endregion implicit conversions to BigRational
  540. // ----- SECTION: private serialization instance methods ----------------*
  541. #region serialization
  542. void IDeserializationCallback.OnDeserialization(Object sender) {
  543. try {
  544. // verify that the deserialized number is well formed
  545. if (m_denominator.Sign == 0 || m_numerator.Sign == 0) {
  546. // n/0 -> 0/1
  547. // 0/m -> 0/1
  548. m_numerator = BigInteger.Zero;
  549. m_denominator = BigInteger.One;
  550. }
  551. else if (m_denominator.Sign < 0) {
  552. m_numerator = BigInteger.Negate(m_numerator);
  553. m_denominator = BigInteger.Negate(m_denominator);
  554. }
  555. Simplify();
  556. }
  557. catch (ArgumentException e) {
  558. throw new SerializationException("invalid serialization data", e);
  559. }
  560. }
  561. [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
  562. void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
  563. if (info == null) {
  564. throw new ArgumentNullException("info");
  565. }
  566. info.AddValue("Numerator", m_numerator);
  567. info.AddValue("Denominator", m_denominator);
  568. }
  569. BigRational(SerializationInfo info, StreamingContext context) {
  570. if (info == null) {
  571. throw new ArgumentNullException("info");
  572. }
  573. m_numerator = (BigInteger)info.GetValue("Numerator", typeof(BigInteger));
  574. m_denominator = (BigInteger)info.GetValue("Denominator", typeof(BigInteger));
  575. }
  576. #endregion serialization
  577. // ----- SECTION: private instance utility methods ----------------*
  578. #region instance helper methods
  579. private void Simplify() {
  580. // * if the numerator is {0, +1, -1} then the fraction is already reduced
  581. // * if the denominator is {+1} then the fraction is already reduced
  582. if (m_numerator == BigInteger.Zero) {
  583. m_denominator = BigInteger.One;
  584. }
  585. BigInteger gcd = BigInteger.GreatestCommonDivisor(m_numerator, m_denominator);
  586. if(gcd > BigInteger.One) {
  587. m_numerator = m_numerator / gcd;
  588. m_denominator = Denominator / gcd;
  589. }
  590. }
  591. #endregion instance helper methods
  592. // ----- SECTION: private static utility methods -----------------*
  593. #region static helper methods
  594. private static bool SafeCastToDouble(BigInteger value) {
  595. return s_bnDoubleMinValue <= value && value <= s_bnDoubleMaxValue;
  596. }
  597. private static bool SafeCastToDecimal(BigInteger value) {
  598. return s_bnDecimalMinValue <= value && value <= s_bnDecimalMaxValue;
  599. }
  600. private static void SplitDoubleIntoParts(double dbl, out int sign, out int exp, out ulong man, out bool isFinite) {
  601. DoubleUlong du;
  602. du.uu = 0;
  603. du.dbl = dbl;
  604. sign = 1 - ((int)(du.uu >> 62) & 2);
  605. man = du.uu & 0x000FFFFFFFFFFFFF;
  606. exp = (int)(du.uu >> 52) & 0x7FF;
  607. if (exp == 0) {
  608. // Denormalized number.
  609. isFinite = true;
  610. if (man != 0)
  611. exp = -1074;
  612. }
  613. else if (exp == 0x7FF) {
  614. // NaN or Infinite.
  615. isFinite = false;
  616. exp = Int32.MaxValue;
  617. }
  618. else {
  619. isFinite = true;
  620. man |= 0x0010000000000000; // mask in the implied leading 53rd significand bit
  621. exp -= 1075;
  622. }
  623. }
  624. private static double GetDoubleFromParts(int sign, int exp, ulong man) {
  625. DoubleUlong du;
  626. du.dbl = 0;
  627. if (man == 0) {
  628. du.uu = 0;
  629. }
  630. else {
  631. // Normalize so that 0x0010 0000 0000 0000 is the highest bit set
  632. int cbitShift = CbitHighZero(man) - 11;
  633. if (cbitShift < 0)
  634. man >>= -cbitShift;
  635. else
  636. man <<= cbitShift;
  637. // Move the point to just behind the leading 1: 0x001.0 0000 0000 0000
  638. // (52 bits) and skew the exponent (by 0x3FF == 1023)
  639. exp += 1075;
  640. if (exp >= 0x7FF) {
  641. // Infinity
  642. du.uu = 0x7FF0000000000000;
  643. }
  644. else if (exp <= 0) {
  645. // Denormalized
  646. exp--;
  647. if (exp < -52) {
  648. // Underflow to zero
  649. du.uu = 0;
  650. }
  651. else {
  652. du.uu = man >> -exp;
  653. }
  654. }
  655. else {
  656. // Mask off the implicit high bit
  657. du.uu = (man & 0x000FFFFFFFFFFFFF) | ((ulong) exp << 52);
  658. }
  659. }
  660. if (sign < 0) {
  661. du.uu |= 0x8000000000000000;
  662. }
  663. return du.dbl;
  664. }
  665. private static int CbitHighZero(ulong uu) {
  666. if ((uu & 0xFFFFFFFF00000000) == 0)
  667. return 32 + CbitHighZero((uint) uu);
  668. return CbitHighZero((uint) (uu >> 32));
  669. }
  670. private static int CbitHighZero(uint u) {
  671. if (u == 0)
  672. return 32;
  673. int cbit = 0;
  674. if ((u & 0xFFFF0000) == 0) {
  675. cbit += 16;
  676. u <<= 16;
  677. }
  678. if ((u & 0xFF000000) == 0) {
  679. cbit += 8;
  680. u <<= 8;
  681. }
  682. if ((u & 0xF0000000) == 0) {
  683. cbit += 4;
  684. u <<= 4;
  685. }
  686. if ((u & 0xC0000000) == 0) {
  687. cbit += 2;
  688. u <<= 2;
  689. }
  690. if ((u & 0x80000000) == 0)
  691. cbit += 1;
  692. return cbit;
  693. }
  694. #endregion static helper methods
  695. } // BigRational
  696. } // namespace Numerics