Search code examples
c#mathfractions

Convert to fraction inches


I need to convert a double value (centimeters) to a fraction value with this format: 3 1/64 (inches). After reading a lot about this and finding algorithms for converting into fractions, I think they are not good for what I need, because my fractions should be in these formats: ?/2, ?/4, ?/8, ?/16, ?/32, ?/64. I have seen conversion tables like this: table. And I think my best solution is to create a key, value list with all values in the table and for each number find the best approximation in the list.

For example: 3.21 cm. = 1.26378 in = 1 in + 0.26378. So, according the table linked, 0.26378 = 17/64. And the final result should be 1 17/64 inches.

So my questions are:

  1. Is a good idea to have a list with the values in the table and find the closest value in order to give the fraction or it is better to create an algorithm for this?

  2. In case it is fine to create a list with the values, how can I find the closest value of a given number in my list?


Solution

  • I suggest using simple math instead of table

    private static string ToFraction64(double value) {
      // denominator is fixed
      int denominator = 64;
      // integer part, can be signed: 1, 0, -3,...
      int integer = (int) value;
      // numerator: always unsigned (the sign belongs to the integer part)
      // + 0.5 - rounding, nearest one: 37.9 / 64 -> 38 / 64; 38.01 / 64 -> 38 / 64
      int numerator = (int) ((Math.Abs(value) - Math.Abs(integer)) * denominator + 0.5);
    
      // some fractions, e.g. 24 / 64 can be simplified:
      // both numerator and denominator can be divided by the same number
      // since 64 = 2 ** 6 we can try 2 powers only 
      // 24/64 -> 12/32 -> 6/16 -> 3/8
      // In general case (arbitrary denominator) use gcd (Greatest Common Divisor):
      //   double factor = gcd(denominator, numerator);
      //   denominator /= factor;
      //   numerator /= factor;
      while ((numerator % 2 == 0) && (denominator % 2 == 0)) {
        numerator /= 2;
        denominator /= 2;
      }
    
      // The longest part is formatting out
    
      // if we have an actual, not degenerated fraction (not, say, 4 0/1)
      if (denominator > 1)
        if (integer != 0) // all three: integer + numerator + denominator
          return string.Format("{0} {1}/{2}", integer, numerator, denominator);
        else if (value < 0) // negative numerator/denominator, e.g. -1/4
          return string.Format("-{0}/{1}", numerator, denominator);
        else // positive numerator/denominator, e.g. 3/8
          return string.Format("{0}/{1}", numerator, denominator);
      else 
        return integer.ToString(); // just an integer value, e.g. 0, -3, 12...  
    }
    

    Tests

    const double cmInInch = 2.54;
    
    // 1 17/64
    Console.Write(ToFraction64(3.21 / cmInInch));
    // -1 17/64
    Console.Write(ToFraction64(-1.26378));
    // 3 1/4
    Console.Write(ToFraction64(3.25001));
    // 3 1/4
    Console.Write(ToFraction64(3.24997));
    // 5
    Console.Write(ToFraction64(5.000001));
    // -1/8
    Console.Write(ToFraction64(-0.129));
    // 1/8
    Console.Write(ToFraction64(0.129));