Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

CFraction - a double / fraction / string conversion class

0.00/5 (No votes)
17 Apr 2000 1  
A class that converts between doubles, strings and fractional representations.

Introduction

The CFraction class is a handy class when dealing with doubles and fractions. You can use it like a double and do anything you would do with a double, plus you assign strings to it. When you need to get a string representation, use one of the methods returning a CString. Or, if you do not use CString, there are methods for char *.

Example usage:

  CFraction F1,F2;
  double D;
  CString S;
 
  // Assignment

  F1 = 5.126;        // double

  F2 = "5.126";      // string

  F1 = "5 63/500";   // string with fraction


  D = F1;            // double = CFraction

  F2 = D;            // CFraction = double


  // Math

  F1 = F2 + D;       // Mix and match them

  F2 = F1/D;
  // any math operation you want


  // Comparison

  if ((F1 > D) || (F1 < F2))
    D = F2/F1;
                      
  F2 = 15.0135;      // 5 27/2000

  
  S = F2.ToString(); // Returns "5 27/2000" 


  // Another version of ToString limits 

  // the denominator to the passed maximum value.

  S = F2.ToString(64); 
  // Returns "15 1/64" because 64 was exceeded

  // with first estimate 1/74 


  // Or, if you are working with stock prices

  // and you only want to show fractions when

  // the deniminator is 2,4,8,16,32,64,128,or 256

  // or up to passed maximum denominator ( <= 256) 

  S = F2.ToStockString(); 
  // Returns "15.0135" because fraction is not a normal stock price  


  // Or, force the denominator to be a valid

  // stock price denominator up to passed Max. Den. 

  S = F2.ForceToStockString();   // Returns closest stock price "15 3/256"

  S = F2.ForceToStockString(64); // Returns closest stock price "15 1/64" 

  
  // If you are like me, you are wondering what it will do with PI :-)

  F1 = 3.1415926535897932384626433832795; 
  // From Calculator - well beyond doubles capability

  S = F1.ToString(); // Returns  "3 3612111/25510582"

  // The difference is about 5.79087e-16 

  
  // You can increase precision when you know

  // that a value has a very large denominator. 

  // A double passed to ToString specifies the allowed error to shoot for. 

  // Warning: Using a smaller allowed error

  // can cause invalid results for fractions that 

  // can be accurately converted using a larger

  // allowed error. Also, since __int64 is used,

  // a denomintor cannot exceed 18 or 19 digits.

  S = F1.ToString(10.0e-50); 
  // Pushing it way up returns "3 36853866/260280919" 

  // The difference to double precision

  // is 0 because doubles accuracy was reached. 

  // The actual difference is about 1.27274e-16. 

  
  // If you have access to a more accurate type (like Calculator does),

  // you can replace double in the code

  // to increase accuracy. You can also 

  // replace __int64 if you want a denominator >  18 digits.


  // Lets try a resonable maximum denominator fo PI, like 999

  S = F1.ToString(999); // returns "3 16/113"


  // The first three estimates this class gives

  // for PI are 3 1/7, 3 15/106, and 3 16/113

The algorithm was developed using the formula for converting a decimal fraction to a string fraction. Any fractional part can be represented as:

1/(I1 + 1/(I2 + 1/(I3 + 1/In ...

where I1 is the integer part of the reciprocal of the fraction and I2 is the integer part of the reciprocal minus I1 ... etc. To determine the integer numerator and denominator, the expression can be reduced. It turns out that the numerator is simpler to calculate than the denominator, so the denominator can simply be determined by dividing the numerator by the original decimal.

Reduction of 1/(I1 + 1/(I2 + 1/(I3 + 1/In ... ))) - There is a way to mathematically reduce this elegantly. However, I think a simple step by step algebraic approach will be understood by more readers.

  • 1 I term: 1/I1 Numerator N1 = 1
  • 2 I terms: 1/(I1 + 1/I2) Multiply by I2/I2 = I2/((I1*I2) + 1),

    Numerator N2 = I2

  • 3 I terms: 1/(I1 + 1/(I2 + 1/I3)) Multiply by I3/I3 = 1/(I1 + I3/(I2*I3) + 1)

    then Multiply by ((I2*I3) + 1)/((I2*I3) + 1) = ((I2*I3) + 1)/(I1*((I2*I3) + 1) + I3),

    Numerator N3 = ((I2*I3) + 1)

  • 4 I terms: ...

    Multiply by I4/I4 = 1/(I1 + 1/(I2 + I4/(I3*I4) + 1))

    Multiply by (I3*I4) + 1) = 1/(I1 + ((I3*I4) + 1)/(I2*((I3*I4) + 1) + I4)).. ,

    Numerator N4 = I2*((I3*I4) + 1) + I4

As we continue, a pattern to the numerators appears showing that N1 = 1, N2 = I2, and Nn = In*Nn-2 + Nn-3 .

This is fairly simple to code.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here