Search code examples
algorithmrational-numbers

Algorithm for detecting repeating decimals?


Is there an algorithm for figuring out the following things?

  1. If the result of a division is a repeating decimal (in binary).
  2. If it repeats, at what digit (represented as a power of 2) does the repetition start?
  3. What digits repeat?

Some examples:

1/2 = 1/10 = 0.1 // 1 = false, 2 = N/A, 3 = N/A, 4 = N/A
1/3 = 1/11 = 0.010101... // 1 = true, 2 = -2, 3 = 10
2/3 = 10/11 = 0.101010... // 1 = true, 2 = -1, 3 = 10
4/3 = 100/11 = 1.010101... // 1 = true, 2 = 0, 3 = 10
1/5 = 1/101 = 0.001100110011... // 1 = true, 2 = -3, 3 = 1100

Is there a way to do this? Efficiency is a big concern. A description of the algorithm would be preferred over code, but I'll take what answer I can get.

It's also worth noting that the base isn't a big deal; I can convert the algorithm over to binary (or if it's in, say base 256 to use chars for ease, I could just use that). I say this because if you're explaining it might be easier for you to explain in base 10 :).


Solution

  • To find the repeating pattern, just keep track of the values you use along the line:

    1/5 = 1/101:
    
    1 < 101 => 0
    (decimal separator here)
    10 < 101 => 0
    100 < 101 => 0
    1000 >= 101 => 1
    
      1000 - 101 = 11
    
    110 >= 101 => 1
    
      110 - 101 = 1
    
    10 -> match
    

    As you reach the same value as you had at the second bit, the process will just repeat from that point producing the same bit pattern over and over. You have the pattern "0011" repeating from the second bit (first after decimal separator).

    If you want the pattern to start with a "1", you can just rotate it until it matches that condition:

    "0011" from the second bit
    "0110" from the third bit
    "1100" from the fourth bit
    

    Edit:
    Example in C#:

    void FindPattern(int n1, int n2) {
       int digit = -1;
       while (n1 >= n2) {
          n2 <<= 1;
          digit++;
       }
       Dictionary<int, int> states = new Dictionary<int, int>();
       bool found = false;
       while (n1 > 0 || digit >= 0) {
          if (digit == -1) Console.Write('.');
          n1 <<= 1;
          if (states.ContainsKey(n1)) {
             Console.WriteLine(digit >= 0 ? new String('0', digit + 1) : String.Empty);
             Console.WriteLine("Repeat from digit {0} length {1}.", states[n1], states[n1] - digit);
             found = true;
             break;
          }
          states.Add(n1, digit);
          if (n1 < n2) {
             Console.Write('0');
          } else {
             Console.Write('1');
             n1 -= n2;
          }
          digit--;
       }
       if (!found) {
          Console.WriteLine();
          Console.WriteLine("No repeat.");
       }
    }
    

    Called with your examples it outputs:

    .1
    No repeat.
    .01
    Repeat from digit -1 length 2.
    .10
    Repeat from digit -1 length 2.
    1.0
    Repeat from digit 0 length 2.
    .0011
    Repeat from digit -1 length 4.