How can I convert a decimal to a fraction?

2020-07-07 03:38发布

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

标签: vba
13条回答
可以哭但决不认输i
2楼-- · 2020-07-07 04:16

I shared an answer at this link : https://stackoverflow.com/a/57517128/11933717

It's also an iterative function, but unlike finding numerator and denominator in a nested loop, it just tests numerators only and so, should be faster.

Here is how it works :

It assumes that, based on the user input x, you want to find 2 integers n / m .

  • n/m = x , meaning that
  • n/x should give an almost integer m

Say one needs to find a fraction for x = 2.428571. Putting the int 2 aside for later, the algo starts by setting n and x and iterates n :

// n / x       = m ( we need m to be an integer )
// n = 1 ; x = .428571 ;
   1 / .428571 = 2.333335 (not close to an integer, n++)
   2 / .428571 = 4.666671 (not close to an integer, n++)
   3 / .428571 = 7.000007

At this point n = 3, we consider that m = 7.000007 is integer enough --based on some kind of accuracy the programmer decides-- and we reply the user

2.428571 =   2  + 3/7 
         = 14/7 + 3/7
         = 17/7
查看更多
欢心
3楼-- · 2020-07-07 04:19

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 ...

查看更多
Luminary・发光体
4楼-- · 2020-07-07 04:21

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.

查看更多
家丑人穷心不美
5楼-- · 2020-07-07 04:22

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.

查看更多
Juvenile、少年°
6楼-- · 2020-07-07 04:22

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.

查看更多
Summer. ? 凉城
7楼-- · 2020-07-07 04:23

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
查看更多
登录 后发表回答