tags:

views:

1332

answers:

12

How do I convert a indefinite decimal (i.e. .333333333...) to a string fraction representation (i.e. "1/3"). I am using VBA and the following is the code I used (i get an overflow error at the line "b = a Mod b":

Function GetFraction(ByVal Num As Double) As String

    If Num = 0# Then
        GetFraction = "None"
    Else
        Dim WholeNumber As Integer
        Dim DecimalNumber As Double
        Dim Numerator As Double
        Dim Denomenator As Double
        Dim a, b, t As Double

        WholeNumber = Fix(Num)
        DecimalNumber = Num - Fix(Num)
        Numerator = DecimalNumber * 10 ^ (Len(CStr(DecimalNumber)) - 2)
        Denomenator = 10 ^ (Len(CStr(DecimalNumber)) - 2)
        If Numerator = 0 Then
            GetFraction = WholeNumber
        Else
            a = Numerator
            b = Denomenator
            t = 0

            While b <> 0
                t = b
                b = a Mod b
                a = t
            Wend
            If WholeNumber = 0 Then
                GetFraction = CStr(Numerator / a) & "/" & CStr(Denomenator / a)
            Else
                GetFraction = CStr(WholeNumber) & " " & CStr(Numerator / a) & "/" & CStr(Denomenator / a)
            End If
        End If
    End If
End Function
+11  A: 

As .333333333 is not 1/3 you will never get 1/3 but instead 333333333/1000000000 if you do not add some clever "un-rounding" logic.

Here is a solution for handling numbers with periodic decimal representation I remember from school.

A number 0.abcdabcd... equals abcd/9999. So 0.23572357... equals 2357/9999 exactly. Just take that many 9s as your pattern is long. 0.11111... equals 1/9, 0.121212... equals 12/99, and so on. So try just searching a pattern and setting the denominator to the corresponding number. Of course you have to stop after some digits because you will never know if the pattern is repeated for ever or just many times. And you will hit the rounding error in the last digit, so you still need some clever logic.

Daniel Brückner
Neat! Learned something new!
mandroid
+1 for learning me some new math skills... :)
rball
A: 

1/ .3333333333 = 3 because 1/3 = .3333333333333, so whatever number you get do this,

double x = 1 / yourDecimal; int y = Math.Ceil(x);

and now Display "1/" + y

Akash Kava
This does not work ... 0.666666666 becomes 1/1.5 ... or 1/2 ... or 1/1 depending on rounding ... ^^
Daniel Brückner
And in any case 0.33333333 != 1/3 - VBA doesn't understand recurring decimals.
Mike Woodhouse
A: 

It is not allways resoluble, since not all decimals are fractions (for example PI or e).

Also, you have to round up to some length your decimal before converting.

eKek0
+1  A: 

I would multiply by 10000000(or whatever you want depending on the precision), then simplify the resulting fraction (ie n*10000000/10000000)

Brann
+1  A: 

You can approximate it. Essentially cycle through all numerators and denominators until you reach a fraction that is close to what you want.

int num = 1;
int den = 1;
double limit == 0.1;
double fraction = num / den;

while(den < 1000000 ) // some arbitrary large denominator
{
    den = den + 1;    
    for(num = 0; num <= den; num++)
    {
        fraction = num / den;
        if(fraction < n + limit && fraction > n - limit)
             return (num + "/" + den);
    }
}

This is slow and a brute force algorithm, but you should get the general idea.

CookieOfFortune
Surly there is a better way than this dumb brute force.
shoosh
How? Your VBA program doesn't know if you have point-three recurring or 0.3333333. This is the first practical attempt at an answer that I've seen.
Mike Woodhouse
A: 

Have you tried this routine?

NoahD
+1  A: 

In general, it'll be easier if you find the repeating part of the rational number. If you can't find that, you'll have a tough time. Let's say the number if 8.45735735735...

The answer is 8 + 45/100 + 735/999/100 = 8 1523/3330.

The whole number is 8. Add 45/100 - which is .45, the part before the repeating part.

The repeating part is 735/999. In general, take the repeating part. Make it the numerator. The denominator is 10^(number of repeating digits) - 1.

Take the repeating part and shift it the appropriate number of digits. In this case, two, which means divide by 100, so 735/999/100.

Once you figure those parts out, you just need some code that adds and reduces fractions using greatest common fractions ...

dbrown0708
+2  A: 

Google for "decimal to fraction" and you'll get about a gazillion results.

I really like this one, because it's simple, has source code (in RPL, similar to Forth, ~25 lines), and is pretty fast (it's written to run on a 4-bit, 4MHz CPU). The docs say:

In a book called Textbook of Algebra by G. Chrystal, 1st edition in 1889, in Part II, Chapter 32, this improved continued fraction algorithm is presented and proven. Odd to tell, Chrystal speaks of it as if it were ancient knowledge.

Ken
+1  A: 

Similar to CookieOfFortune's, but it's in VB and doesn't use as much brute force.

Dim tolerance As Double = 0.1   'Fraction has to be at least this close'
Dim decimalValue As Double = 0.125  'Original value to convert'
Dim highestDenominator = 100   'Highest denominator you`re willing to accept'

For denominator As Integer = 2 To highestDenominator - 1
    'Find the closest numerator'
    Dim numerator As Integer = Math.Round(denominator * decimalValue)

    'Check if the fraction`s close enough'
    If Abs(numerator / denominator - decimalValue) <= tolerance Then
        Return numerator & "/" & denominator
    End If
Next

'Didn't find one.  Use the highest possible denominator'
Return Math.Round(denominator * decimalValue) & "/" & highestDenominator

...Let me know if it needs to account for values greater than 1, and I can adjust it.

EDIT: Sorry for the goofed up syntax highlighting. I can't figure out why it's all wrong. If someone knows how I can make it better, please let me know.

The highlighter doesn't understand VB trailing single-quote comments, so it messes up while it looks for a closing quote. I added some, and used back-ticks for the apostrophes, which looks a bit nicer...
Mike Woodhouse
A: 

This site seem to have a really nice implementation of this in JavaScript.

shoosh
+1  A: 

Python has a nice routine in its fractions module. Here is the working portion that converts a n/d into the closest approximation N/D where D <= some maximum value. e.g. if you want to find the closest fraction to 0.347, let n=347,d=1000 and max_denominator be 100 and you will obtain (17, 49) which is as close as you can get for denominators less than or equal to 100. The '//' operator is integer division so that 2//3 gives 0, i.e. a//b = int(a/b).

def approxFrac(n,d,max_denominator):

    #give a representation of n/d as N/D where D<=max_denominator
    #from python 2.6 fractions.py
    #
    # reduce by gcd and only run algorithm if d>maxdenominator
    g, b = n, d
    while b:
        g, b = b, g%b
    n, d = n/g, d/g
    if d <= max_denominator:
        return (n,d)
    nn, dd = n, d
    p0, q0, p1, q1 = 0, 1, 1, 0
    while True:
        a = nn//dd
        q2 = q0+a*q1
        if q2 > max_denominator:
            break
        p0, q0, p1, q1 = p1, q1, p0+a*p1, q2
        nn, dd = dd, nn-a*dd

    k = (max_denominator-q0)//q1
    bound1 = (p0+k*p1, q0+k*q1)
    bound2 = (p1, q1)
    if abs(bound2[0]*d - bound2[1]*n) <= abs(bound1[0]*d - bound1[1]*n):
        return bound2
    else:
        return bound1
+1  A: 

This only works in Excel-VBA but since you had it tagged "VBA" I will suggest it. Excel has a custom "fraction" format that you can access via "Format Cells" (or ctrl-1 if you prefer). This particular number format is Excel-Specific and so does not work with the VBA.Format function. It does however work with the Excel Formula TEXT(). (Which is the Excel equivalent of VBA.Format. This can be accessed like So:

Sub Example()    
    MsgBox Excel.WorksheetFunction.Text(.3333,"# ?/?")
End Sub

To show more than one digit (Example 5/12) just up the number of question marks.

Oorang