Android Code Snippet 0.0344827586206896551724137931 = 1/29

If the title of
this snippet in any way intrigues you then here is the routine which did that conversion
You are, however, warned that however intriguing it might be it is all but totally useless .....

Conversion of decimal numbers to neat integer fractions is something you may have learned at school. The basic process is easy. Given 3.65 we just push the dot two places to the right, which means multiplying by 100, then use a divisor (denominator) of 100 and, voila, we have 365/100. We can see, however, that both numerator and denominator are divisible by 5 so the result can be simplified to 73/20.

But what if the number is a never-ending recurring decimal like 0.66666666... ? There's a well-known way to manage that (look at which explains and illustrates it) but it doesn't work properly for non-terminating numbers that do not have a recurring part or whose recurring part is very long.

The algorithm in this snippet works for all types of numbers and is radically simpler than other techniques. As a bonus, it tests the number to see if it is a factor of a set of well-known constants, like π, e, or φ or low order prime roots - √2, √3 and √5. Because it is interesting (to me!) I've thrown in a test for π² as well.

The main algorithm manipulates two Double values which we call n and d. We start a loop and initialise it with d = 1 and n = our number of interest. In each iteration of the loop we divide d by n to get the next value for d and take the fractional part of the reciprocal of n for its next value. In essence it's just two lines:
d = d / n
n = (1/n) - Floor(1/n)
As the Loop progresses n gets smaller and d gets larger. We exit the loop when either n is tiny or d is very large. The choice of values used as limits for the loop determines the precision of the results. With the limits used here (n>0.01 and d<10000) accuracy is excellent (e.g 0.0588235294117647 = 1/17). The algorithm is concise, lucid, and fast. It gets 'long period' recurrences exactly right - 0.0344827586206896551724137931 = 1/29. The online tool at returns 344827586206 / 9999999999999 partly because it has insufficient input precision but also because it uses an algorithm that doesn't cope with long period recurrences.

We round the results to integers using a value denoted by q. This effectively limits results to shorter denominators. Longer denominators will not be very 'useful' but shorter ones, even though slightly less accurate, are more so. q can be any value above 0 up to about 15 before it causes overflows. A value of 5 gives quite concise and useful approximations.

Recurring decimals: In maths we use dotted or overline (vinculum) notation to show recurring digits but we can't easily input that on a standard keyboard. In this code we allow the use of any non-numeric character (other than a dot) to mark the start of a recurring sequence, for example the caret ^, pipe | or even just a space. So a string input of '0.6' will return 3/5 but '0. 6' will return 2/3. Recurring decimals also need adequate precision to yield accurate results. To ensure this without forcing long input strings we fill out the string in code with the recurring part until it is at least 16 digits long.

Well-known constants: This code returns a set of results that shows how the number relates to the well-known constants √2, √3, √5, φ, e, π and π². (φ = 'golden mean' = (1+√5)/2 with the properties: φ-1 = 1/φ and φ+1 = φ².) For example, 3.14159 = π. All the results are returned in an 8x3 dimensioned array. The first element of each of the 8 results is a flag used to mark overflow where no real result is calculable ('X' or blank). The second element is the factor symbol (blank, √2, √3, √5, φ, e, π or π²). The third element is the result or an empty string if non-calculable.

There is no attempt to produce a particularly elegant visual output. However, the code includes a short visual formatting routine which adjusts the size and colour of the text of each result according to the 'quality' of the approximation the algorithm has achieved. You can comment out the line with "X" to see all results even if they are not 'useful' ones, in which case blank lines will be shown for non-calculable results.

The code uses no libraries or the Visual Designer - you can just copy and paste this block:

B4X PointLess Answers:
#Region  Project Attributes
    #ApplicationLabel: PointLess Answers
    #VersionCode: 0
    #VersionName: 26_06_20
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    Dim saveInput As String
    Dim saveOutput As String
End Sub

Sub Globals
    Dim lbl As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'base panel
    Dim rootPanel As Panel
    rootPanel.Color = Colors.LightGray

  'input field            
    Dim et As EditText
    et.SingleLine = True
    et.Text = saveInput
  'output label          
    lbl.TextColor = Colors.White
    lbl.TextSize = 1
    lbl.Gravity = Gravity.CENTER_HORIZONTAL + Gravity.CENTER_VERTICAL
    Dim cd As ColorDrawable
    cd.Initialize(0xff880000, 25dip)
    lbl.Background = cd
    lbl.Text = saveOutput
End Sub

Sub Activity_Pause(UserClosed As Boolean)
End Sub

Sub Activity_Resume
    lbl.Text = FormatResults(saveInput)
End Sub

Sub ETname_EnterPressed
    Dim etCtl As EditText = Sender
    saveInput = etCtl.Text
    lbl.Text = FormatResults(saveInput)
End Sub

Sub FormatResults(InStr As String) As CSBuilder
    Private    cs As    CSBuilder
    cs.Initialize.Bold.Append(InStr & CRLF & "=" & CRLF).Pop
    Dim h(,) As String = SS_dec2frc(InStr)
    Dim len As Int, fsz As Double
    For m = 0 To h.Length -1
        len = h(m,2).Length
        If h(m,0) = "X" Or len > 10 Then Continue
        fsz = 1.5 - (len*0.05)
        If len <= 6 Then : cs.Color(Colors.Yellow).RelativeSize(fsz)
        Else : cs.Color(0xFFd0FFd0).RelativeSize(fsz) : End If
        cs.Append(h(m,2) & CRLF).PopAll
    Return cs
End Sub

Sub SS_dec2frc(InStr As String) As String(,)
    'split input string into integer, non-recurring, & recurring parts
    InStr = InStr.Trim
    Dim dot As Boolean = False
    Dim s() As String = Array As String("","","")
    Dim c As Char
    For b = 0 To InStr.length-1
        c = InStr.CharAt(b)
        If Asc(c) <> Asc(".") And (Asc(c) < Asc("0") Or Asc(c) > Asc("9")) Then
            If dot = True Then s(2) = InStr.SubString(b+1)
        Else If Asc(c) = Asc(".") Then
            dot = True
        Else If dot = True Then
            s(1) = s(1) & c
            s(0) = s(0) & c
        End If
    'check it's a valid number - return blanks if not
    Dim ret(8,3) As String
    Dim out As String = s(0).Trim & "." & s(1).Trim & s(2).Trim
    If IsNumber(out) = False Then Return ret

    'extend any recurring digits until length is at least 16
    If s(2).Length > 0 Then
        Do Until s(2).Length >= 16
            s(2) = s(2) & s(2)
    End If
    Dim InDub As Double = s(0) & "." & s(1) & s(2)
    Dim ItDub As Double = InDub
    Dim facSym As String = ""
    Dim q As Int = 4
  'loop the algorithm 8 times for the
    'basic and 7 other factor results
    For i = 0 To 7
        facSym = ""
        Select i
            Case 1 : facSym = "√2" : ItDub = Round2(InDub/Sqrt(2),q)
            Case 2 : facSym = "√3" : ItDub = Round2(InDub/Sqrt(3),q)
            Case 3 : facSym = "√5" : ItDub = Round2(InDub/Sqrt(5),q)
            Case 4 : facSym = "φ"  : ItDub = Round2(InDub/((1+Sqrt(5))/2),q)
            Case 5 : facSym = "e"  : ItDub = Round2(InDub/cE,q)
            Case 6 : facSym = "π"  : ItDub = Round2(InDub/cPI,q)
            Case 7 : facSym = "π²" : ItDub = Round2(InDub/(cPI*cPI),q)
        End Select
        'main algorithm
    Dim nmult As Double = Abs(ItDub)
    Dim denom As Double = 1
       Do While nmult > 0.01 And denom < 10000
        denom = denom/nmult
        nmult = (1/nmult) - Floor(1/nmult)
      Dim n As Int = Round2(ItDub*denom,0)
      Dim d As Int = Round2(denom,0)
        '(overflow flag, factor symbol, fraction)
        ret(i,0) = ""
        ret(i,1) = facSym
        ret(i,2) = n & facSym
      'simplify n & d to avoid showing 1 * facSym or /1
        If n=1 And facSym <> "" Then ret(i,2) = facSym
        If d<>1 Then ret(i,2) = ret(i,2) & " / " & d
        If n<1 Or d<1 Then
            ret(i,0) = "X"
            ret(i,1) = facSym
            ret(i,2) = ""
        End If

    Return ret
End Sub
Last edited: