Blake O'Hare .com

Python Script to Detect a Repeating Decimal

Remember doing long division as a 2nd grader? And if you found that the decimal started to repeat, you stopped and drew a little line over the repeating portion? Ah, yes, those were the days. Anyways, this Python function does that sort of division. The returned type is just a string. Repetition detection can be toggled on or off, but if you turn it off, you need to include a limit to the number of digits after the decimal that ought to be returned. Also, if you pass in a huge denominator, it's probably a good idea to include a digit limit anyway even if you want to detect repetitions since those repeating portions can get pretty long.

The repeating portion will appear in brackets. Like so:
>>> divide(15, 7)
2.[142857]


Decimals that needed to be truncated are followed by an ellipsis:
>>> divide(15, 7, True, 4)
2.1428...


If it finishes without finding a repeating decimal, then...well...the result isn't that confusing:
>>> divide(1, 20)
0.05


And here's the code:
def divide(numerator, denominator, detect_repetition=True, digit_limit=None):

    # If repetition detection is off, you must
    # specify a limit on the digits returned
    if not detect_repetition and digit_limit == None:
        return None

    decimal_found = False
    
    v = numerator // denominator
    numerator = 10 * (numerator - v * denominator)
    answer = str(v)
    
    if numerator == 0:
        return answer
    
    answer += '.'
    
    # Maintain a list of all the intermediate numerators
    # and the length of the output at the point where that
    # numerator was encountered. If you encounter the same
    # numerator again, then the decimal repeats itself from
    # the last index that numerator was encountered at.
    states = {}
    
    while numerator > 0 and (digit_limit == None or digit_limit > 0):
        
        if detect_repetition:
            prev_state = states.get(numerator, None)
            if prev_state != None:
                start_repeat_index = prev_state
                non_repeating = answer[:start_repeat_index]
                repeating = answer[start_repeat_index:]
                return non_repeating + '[' + repeating + ']'
            states[numerator] = len(answer)
        
        v = numerator // denominator
        answer += str(v)
        numerator -= v * denominator
        numerator *= 10
        if digit_limit != None:
            digit_limit -= 1
    
    if numerator > 0:
        return answer + '...'
    return answer