Results 1 to 16 of 16

Thread: Resolved: Decimals to Fractions

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Jun 2001
    Posts
    521

    Resolved: Decimals to Fractions

    Does anyone know of any good code to convert decimals to fractions? I've come up with a way, but it's kind of sloppy...

    Any help would be appreciated.
    Last edited by VictorB212; Jul 4th, 2001 at 11:02 PM.

  2. #2
    pathfinder NotLKH's Avatar
    Join Date
    Apr 2001
    Posts
    2,397
    Up to How Many Decimal Places?

    -Lou

  3. #3

    Thread Starter
    Fanatic Member
    Join Date
    Jun 2001
    Posts
    521
    To VB's limit of accuracy. For example, it could figure out the fraction from the VB's result of 123/234. In other words, if I get the solution from 123/234 in the immediate window, and fed it to the function, it would give me the fraction back (reduced form of course ). Is that clear enough?

  4. #4
    Fanatic Member Kaverin's Avatar
    Join Date
    Oct 2000
    Posts
    930
    I don't know how how sloppy your idea of sloppy is (I can sure guess, being guilty of making slop myself), but this works just to get a fraction. It might even be the a similar thing to what you do. It won't do the LCD on the numerator and denominator though. I thought I had LCD/GCF functions around somewhere, but I can't find em anymore .
    VB Code:
    1. Public Function GetFraction(ByVal dblDecimal As Double) As String
    2.    Dim lngCount As Long
    3.    GetFraction = ""
    4.    lngCount = 0
    5.    Do While InStr(CStr(dblDecimal), ".")
    6.       dblDecimal = dblDecimal * 10
    7.       lngCount = lngCount + 1
    8.    Loop
    9.    GetFraction = CStr(dblDecimal) & "/" & CStr(10 ^ lngCount)
    10. End Function
    This will handle something with up to 14 decimal places, and then it goes screwy and puts in the scientific notation. The fraction is still accurate though, but not in a pretty form anymore
    Last edited by Kaverin; Jul 3rd, 2001 at 11:26 PM.
    I'm baaaack...
    VB5 Professional Edition, VC++ 6
    Using a 1 gHz Thunderbird, 256 mb RAM, 40 gb HD system with Win98se

    I feel special because I finally figured out how to loop midis: Post link
    I'm a fanatic too

  5. #5

    Thread Starter
    Fanatic Member
    Join Date
    Jun 2001
    Posts
    521
    That's too sloppy. If I give it .666666666666 it doesn't give me 2/3.

    Here's my code:
    VB Code:
    1. Public Function QuickSort(vData As Variant, Low As Long, Hi As Long)
    2.   If Not IsArray(vData) Then Exit Function
    3.  
    4.   Dim lTmpLow As Long
    5.   Dim lTmpHi As Long
    6.   Dim lTmpMid As Long
    7.   Dim vTempVal As Variant
    8.   Dim vTmpHold As Variant
    9.  
    10.   lTmpLow = Low
    11.   lTmpHi = Hi
    12.  
    13.   If Hi <= Low Then Exit Function
    14.  
    15.   lTmpMid = (Low + Hi) \ 2
    16.      
    17.   vTempVal = vData(lTmpMid)
    18.      
    19.   Do While (lTmpLow <= lTmpHi)
    20.  
    21.      Do While (vData(lTmpLow) < vTempVal And lTmpLow < Hi)
    22.            lTmpLow = lTmpLow + 1
    23.      Loop
    24.      
    25.      Do While (vTempVal < vData(lTmpHi) And lTmpHi > Low)
    26.            lTmpHi = lTmpHi - 1
    27.      Loop
    28.            
    29.      If (lTmpLow <= lTmpHi) Then
    30.          vTmpHold = vData(lTmpLow)
    31.          vData(lTmpLow) = vData(lTmpHi)
    32.          vData(lTmpHi) = vTmpHold
    33.          lTmpLow = lTmpLow + 1
    34.          lTmpHi = lTmpHi - 1
    35.      End If
    36.      
    37.   Loop
    38.          
    39.   If (Low < lTmpHi) Then
    40.       QuickSort vData, Low, lTmpHi
    41.   End If
    42.          
    43.   If (lTmpLow < Hi) Then
    44.        QuickSort vData, lTmpLow, Hi
    45.   End If
    46.  
    47. End Function
    48.  
    49. Public Function cFrac(Expr As Double) As String
    50.  
    51.     Dim i As Long, j As Long
    52.     Dim v As Variant
    53.     Dim x As Double
    54.     Dim curVal As Double
    55.     Dim minVal As Double
    56.     Dim intVal As Double
    57.     Dim idx As Long
    58.     Dim s As String
    59.     Dim arr() As String
    60.     Dim arrIdx As Long
    61.    
    62.     ReDim arr(0)
    63.    
    64.     x = Expr
    65.     intVal = Int(x)
    66.     x = x - intVal
    67.     For i = 1 To 3000
    68.         For j = 1 To i
    69.             If Abs(x - j / i) < 0.00001 Then
    70.                 arr(arrIdx) = String$(4 - Len(CStr(j)), "0") & j & "/" & i
    71.                 arrIdx = arrIdx + 1
    72.                 ReDim Preserve arr(arrIdx)
    73.             End If
    74.         Next
    75.     Next
    76.    
    77.    
    78.     arrIdx = arrIdx - 1
    79.     ReDim Preserve arr(arrIdx)
    80.    
    81.     QuickSort arr, 0, arrIdx
    82.    
    83.     minVal = 0.0001
    84.     For i = 0 To arrIdx
    85.         v = Split(arr(i), "/")
    86.         curVal = Abs(v(0) / v(1) - x)
    87.         If curVal < minVal Then
    88.             minVal = curVal
    89.             idx = i
    90.         End If
    91.     Next
    92.    
    93.     s = arr(idx)
    94.     i = 1
    95.     Do Until Mid(s, i, 1) > 0
    96.         s = Right$(s, Len(s) - 1)
    97.     Loop
    98.     v = Split(s, "/")
    99.     v(0) = v(0) + v(1) * intVal
    100.     s = v(0) & "/" & v(1)
    101.     cFrac = s
    102.     Beep
    103. End Function
    The decimal 0.00001 can be adjusted.

  6. #6
    Fanatic Member Kaverin's Avatar
    Join Date
    Oct 2000
    Posts
    930
    I messed with it some more, and fixed that SF weirdness. The fix is in bright blue:
    VB Code:
    1. Public Function GetFraction(ByVal dblDecimal As Double) As String
    2.    Dim lngCount As Long
    3.    [color=blue]Dim strDenominator As String
    4.    Dim intNumZeroes As Integer[/color]
    5.    GetFraction = ""
    6.    lngCount = 0
    7.    Do While InStr(CStr(dblDecimal), ".")
    8.       dblDecimal = dblDecimal * 10
    9.       lngCount = lngCount + 1
    10.    Loop
    11. [color=blue]   strDenominator = CStr(10 ^ lngCount)
    12.    If InStr(1, strDenominator, "E", vbTextCompare) Then
    13.       intNumZeroes = Val(Right$(strDenominator, Len(strDenominator) - InStr(strDenominator, "+")))
    14.       strDenominator = "1" & String$(intNumZeroes, "0")
    15.    End If[/color]
    16.    GetFraction = CStr(dblDecimal) & "/" & [color=blue]strDenominator[/color]
    17. End Function
    I'm baaaack...
    VB5 Professional Edition, VC++ 6
    Using a 1 gHz Thunderbird, 256 mb RAM, 40 gb HD system with Win98se

    I feel special because I finally figured out how to loop midis: Post link
    I'm a fanatic too

  7. #7

    Thread Starter
    Fanatic Member
    Join Date
    Jun 2001
    Posts
    521
    That won't give you "2/3" for .666666666666 though...

  8. #8
    Fanatic Member Kaverin's Avatar
    Join Date
    Oct 2000
    Posts
    930
    You do have a point there about the 2/3 thing, but when you do that mentally, you've changed the value. I know mathematically that 0.666666... = 2/3 though. It's very easy to prove with some algebra. The thing I mentioned though does it exactly. I just wish I could remember a fast way to get the LCD of the numbers, then what I mentioned might be usable . I just did some other checking, and if you're going for the best significance, yours gets a little fudgy past 6 places. 4*atn(1) (3.14159265358979 - as significant as VB will report pi) will come out as 355/113 with your function. But if you do that expression, you get only 3.141593 back.
    Last edited by Kaverin; Jul 3rd, 2001 at 11:51 PM.
    I'm baaaack...
    VB5 Professional Edition, VC++ 6
    Using a 1 gHz Thunderbird, 256 mb RAM, 40 gb HD system with Win98se

    I feel special because I finally figured out how to loop midis: Post link
    I'm a fanatic too

  9. #9

    Thread Starter
    Fanatic Member
    Join Date
    Jun 2001
    Posts
    521
    Did you try the code I posted?

  10. #10

    Thread Starter
    Fanatic Member
    Join Date
    Jun 2001
    Posts
    521

    Bingo

    Check this code out (adjust the constant for accuracy/speed):
    VB Code:
    1. Public Function CFrac2(ByVal X As Double) As String
    2.  
    3.     Dim Q As Double 'Quotient
    4.     Dim D As Long   'Denominator
    5.     Dim N As Long   'Numberator
    6.    
    7.     D = 1
    8.     N = 1
    9.    
    10.     Q = N / D
    11.    
    12.     Do While Abs(Q - X) > 0.00000000001
    13.        If (Q < X) Then
    14.           N = N+ 1
    15.        Else
    16.           D = D + 1
    17.        End If
    18.        Q = N / D
    19.     Loop
    20.    
    21.     CFrac2 = CStr(N) & "/" & CStr(D)
    22. End Function
    Last edited by VictorB212; Jul 4th, 2001 at 12:54 AM.

  11. #11
    Registered User Nucleus's Avatar
    Join Date
    Apr 2001
    Location
    So that's what you are up to ;)
    Posts
    2,530
    Nice Vic

    Just added support for negative fractions and zero.


    VB Code:
    1. Public Function CFrac2(ByVal Y As Double) As String
    2.  
    3.     Dim Q As Double 'Quotient
    4.     Dim D As Long   'Denominator
    5.     Dim N As Long   'Numerator
    6.     Dim X As Double
    7.    
    8.     If Y <> 0 Then
    9.        
    10.         X = Abs(Y)
    11.    
    12.         D = 1
    13.         N = 1
    14.    
    15.         Q = D / N
    16.    
    17.         Do While Abs(Q - X) > 0.00000000001
    18.             If (Q < X) Then
    19.                 D = D + 1
    20.             Else
    21.                 N = N + 1
    22.             End If
    23.             Q = D / N
    24.         Loop
    25.    
    26.         If Y < 0 Then CFrac2 = "-"
    27.         CFrac2 = CFrac2 & CStr(D) & "/" & CStr(N)
    28.        
    29.     Else
    30.    
    31.         CFrac2 = 0
    32.        
    33.     End If
    34.        
    35. End Function

  12. #12
    Fanatic Member Kaverin's Avatar
    Join Date
    Oct 2000
    Posts
    930
    That's probably the best way to do it. After playing with mine, I managed to get it to give exact fractions, but I ended up having to make up other functions to get around some limits I was using Mod in my function to get the GCF in order to reduce the fraction, and needed to make something to handle doubles. After that then, I could get the scientific notation thing to creep up in the numerator with values with enough decimal places, so I figured the effort was starting to outweigh the use. Running along side each other though, and timing with GetTickCount, I kept getting 0 out of mine, and up to .5 s with yours. I would save the GTC, then do the function, and immediately get GTC once more for the timing. This is what I ended up using as mine (without the SF removal part). If anything, at least you may find the GCF function useful later. It uses Euclid's algorithm.
    VB Code:
    1. Public Function GetFraction(ByVal dblDecimal As Double) As String
    2.    Dim lngCount As Long
    3.    Dim dblDenominator As Double, dblNumerator As Double
    4.    Dim dblGCF As Double
    5.    GetFraction = ""
    6.    lngCount = 0
    7.    Do While InStr(CStr(dblDecimal), ".")
    8.       dblDecimal = dblDecimal * 10
    9.       lngCount = lngCount + 1
    10.    Loop
    11.    dblDenominator = 10 ^ lngCount
    12.    dblGCF = GCF(dblDecimal, dblDenominator)
    13.    dblDenominator = dblDenominator / dblGCF
    14.    dblNumerator = dblDecimal / dblGCF
    15.    GetFraction = CStr(dblNumerator) & "/" & CStr(dblDenominator)
    16. End Function
    17.  
    18. Public Function GCF(ByVal dblX As Double, ByVal dblY As Double) As Double
    19.    Dim dblPass As Double
    20.    dblX = Abs(dblX)
    21.    dblY = Abs(dblY)
    22.    Do While dblX > 0
    23.       dblPass = dblX
    24.       dblX = DBLMod(dblY, dblX)
    25.       dblY = dblPass
    26.    Loop
    27.    GCF = dblPass
    28. End Function
    29.  
    30. Public Function DBLMod(ByVal dblNumber As Double, ByVal dblDivisor As Double) As Double
    31.    DBLMod = dblNumber - (Int(dblNumber / dblDivisor) * dblDivisor)
    32. End Function
    It seems to work for around 10-12 decimal places or so with some variance. Then the SF creeps in.
    I'm baaaack...
    VB5 Professional Edition, VC++ 6
    Using a 1 gHz Thunderbird, 256 mb RAM, 40 gb HD system with Win98se

    I feel special because I finally figured out how to loop midis: Post link
    I'm a fanatic too

  13. #13

    Thread Starter
    Fanatic Member
    Join Date
    Jun 2001
    Posts
    521

    Nucleus

    This code came to me once I looked at the above code I had made. My original code was testing a lot of stuff multiple times (2/3,4/6,6/9, etc.), so I looked for a way to speed it up, and this "zeroing in" method works beautifully. I wonder if putting it in C++ will make it faster...

    I found a pretty good fraction for pi using a precision of 1E-16:
    Code:
    245850922/78256779
    I think I can mark this problem resolved. Maybe I could start a trend; it could help when searching for stuff (to know if the problem in the thread was resolved).

  14. #14
    ALFWare
    Guest

    I FOUND SOME GREAT CODE!!!!!!

    This code is lightning fast, I mean like it doesn't even take a second, I have tried lots of code all that do loop and counts down by one and all this, but this is freaky.

    Try it I promise you it works also
    .33333 returns 33333/100000
    whereas
    .333333 return 1/3
    same for all your reoccurances like:
    .123123123..... returns 41/33


    Code:
    ' Converts a decimal value into fractional parts as integers
    ' (based on the concept of Continued Fractions)
    
    ' Examples of usage:
    
    ' Call DeclToFrac(0.125, a, b)   ' 1 and 8 are returned in a & b
    ' Call DecToFrac(5/40, a, b)     ' 1 and 8 are also returned
    ' Call DecToFrac(2/3, a, b)      ' 2 and 3 are returned
    
    
    
    ' Since more than one value needs to be returned, they are returned
    ' to variables which are passed by reference as arguments (Numerator
    ' and Denom) to the DecToFrac Sub procedure
    
    Sub DecToFrac(DecimalNum As Double, Numerator As Long, Denom As Long)
    
       ' The BigNumber constant can be adjusted to handle larger fractional parts
       Const BigNumber = 50000
       Const SmallNumber = 1E-16
    
       Dim Inverse As Double, FractionalPart As Double
       Dim WholePart As Long, SwapTemp As Long
    
       Inverse = 1 / DecimalNum
       WholePart = Int(Inverse)
       FractionalPart = Frac(Inverse)
    
       If 1 / (FractionalPart + SmallNumber) < BigNumber Then
            ' Notice that DecToFrac is called recursively.  
            Call DecToFrac(FractionalPart, Numerator, Denom)
            Numerator = Denom * WholePart + Numerator
    
            SwapTemp = Numerator
            Numerator = Denom
            Denom = SwapTemp
       Else ' If 1 / (FractionalPart + SmallNumber) > BigNumber
            ' Recursion stops when the final value of FractionalPart is 0 or
            ' close enough.  SmallNumber is added to prevent division by 0.
            Numerator = 1
            Denom = Int(Inverse)
       End If
    End Sub
    
    ' This function is used by DecToFrac and DecToProperFact
    
    Function Frac(x As Double) As Double
        Frac = Abs(Abs(x) - Int(Abs(x)))
    End Function
    
    ' This additional procedure handles "improper" fractions and returns
    ' them in mixed form (a b/c) when the numerator is larger than the denominator
    
    Sub DecToProperFrac(x As Double, a As Long, b As Long, c As Long)
       If x > 1 Then a = Int(x)
       If Frac(x) <> 0 Then 
          Call DecToFrac(Frac(x), b, c)
       End If
    End Sub
    
    '#####################################################################
    '#
    '# This item has been brought to you by Daniel Corbier, the author of
    '# UCalc Fast Math Parser, a component which allows programs to
    '# evaluate expressions that are defined at runtime. You can learn
    '# more and download a fully functional copy at www.ucalc.com/mathparser
    '#
    '#####################################################################

  15. #15
    Frenzied Member
    Join Date
    Jul 1999
    Location
    Huntingdon Valley, PA 19006
    Posts
    1,151
    If you are willing to write code that checks for repeating patterns, it is easy to generate the formula which will convert to rational numbers. Patterns like the following are pretty straight forward.

    1/3 = .3333 (3/10)/.9 = 3/9
    2/3 = .66666 (6/10)/.9 = 6/9
    4/33 = .12121212 (12/100)/.99 = 12/99
    1/7 = .142857142857 (142857/1000000)/.999999 = 142857/999999

    You need a function which can find common factors and divide them out.

    If there is no pattern, the best you can do is the following

    abc/1000 = .abc
    abcd/10000 = .abcd
    abcde/100000 = .abcde
    Live long & prosper.

    The Dinosaur from prehistoric era prior to computers.

    Eschew obfuscation!
    If a billion people believe a foolish idea, it is still a foolish idea!
    VB.net 2010 Express
    64Bit & 32Bit Windows 7 & Windows XP. I run 4 operating systems on a single PC.

  16. #16
    ALFWare
    Guest

    ???

    That code isn't just for reoccuring decimals... it converts any fraction and does it very well, with the Lowest Common Denominator.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width