6

Rather than converting an arbitrary decimal to an exact fraction (something like 323527/4362363), I am trying to convert to just common easily-discernible (in terms of human-readability) quantities like 1/2, 1/4, 1/8 etc.

Other than using a series of if-then, less than/equal to etc comparisons, are there more optimized techniques to do this?

Edit: In my particular case, approximations are acceptable. The idea is that 0.251243 ~ 0.25 = 1/4 - in my usage case, that's "good enough", with the latter more preferable for human readability in terms of a quick indicator (not used for calculation, just used as display numerics).

ina
  • 19,167
  • 39
  • 122
  • 201
  • Your question is vague. How do you define human-readability? And more importantly, what if there is no human-readable equivalent form? Do you allow approximations? I personally don't see any easy way to write your example, 323526/4362363, in human readable form without resorting to approximation. – Ehsan Foroughi Jan 13 '11 at 03:45
  • approximations are acceptable - 4 digit decimal accuracy is way more than enough – ina Jan 13 '11 at 03:46
  • there is something else to "human readable" though, that just the length of either numbers. Generally fractions of the form "1/x" are *easy* but "4/85" is just plain weird and would be better expressed as "1/41". Of course, the "1/x" family only works well for number smaller than 0.5, and approximating 0.4 using means a great loss... perhaps that a percentage representation would be adequate ? – Matthieu M. Jan 13 '11 at 07:41
  • (whole number) + x/y where x and y are each less than 10 – ina Jan 13 '11 at 07:43

5 Answers5

7

Look up "continued fraction approximation". Wikipedia has a basic introduction in its "continued fraction" article, but there are optimized algorithms that generate the approximated value while generating the fraction.

Then pick some stopping heuristic, a combination of size of denominator and closeness of approximation, for when you're "close enough".

Anonymous
  • 71
  • 1
3

You can use Euclidean algorithm to get Greatest Common Divisor between enumerator and denominator and divide them by it.

Nylon Smile
  • 8,990
  • 1
  • 25
  • 34
  • I'd also recommend limiting your denominator to something reasonable to avoid cases like 107842/1454121 (which is your example, reduced). – coreyward Jan 13 '11 at 03:34
  • mea culpa - typo'd that initial fraction... meant a 7 instead of a 6 ;-) – ina Jan 13 '11 at 03:49
  • Why are people voting this up? This doesn't answer the question at all. He's asking about approximating decimals to simple fractions, not about reducing fractions. – Keith Irwin Jan 16 '11 at 02:25
  • You're right, after the edit to the question, my answer doesn't make sense. – Nylon Smile Jan 16 '11 at 10:40
0

In the following, I'm going to assume that our decimals fall in between 0 and 1. It should be straightforward to adapt this to larger numbers and negative numbers.

Probably the easiest thing to do would be to choose the largest denominator that you would find acceptable and then create a list of fractions between 0 and 1 which have that denominators less than or equal to them. Be sure to avoid any fractions which can be simplified. Obviously, once you've listed 1/2, you don't need 2/4. You can avoid fractions which can be simplified by checking that the GCD of the numerator and denominator is 1 suing Euclid's algorithm. Once you have your list. Evaluate these as floating point numbers (probably doubles, but the data type obviously depends on your choice of programming language). Then insert them into a balanced binary search tree storing both the original fraction and the floating point evaluation of the fraction. You should only need to do this once to set things up initially so the n*log(n) time (where n is the number of fractions) isn't very much.

Then, whenever you get a number, simply search the tree to find the closest number to it which is in the search tree. Note that this is slightly more complicated than searching for an exact match because the node you're looking for may not be a leaf node. So, as you traverse the tree keep a record of the closest valued node that you have visited. Once you reach a leaf node and compare that one to your closest valued node that you have visited, you are done. Whichever your closest one is, it's fraction is your answer.

Keith Irwin
  • 5,628
  • 22
  • 31
0

Here is a suggestion: Assuming your starting fraction is p/q

  1. Calculate r = p/q as a rational(floating point) value (e.g. r = float(p)/float(q))

  2. Calculate the rounded decimal x = int(10000*r)

  3. Calculate GCD (greatest common denominator) of x and 10000: s = GCD(x, 10000)

  4. Represent the result as m / n where m = x/s and n = y/s (your example computes to 371 / 5000)

Normally, all denominators of 1000 are fairly human readable.

This might not provide the best result when the value is closer to simpler cases such as 1/3. However, I personally find 379/1000 much more human readable than 47/62 (which is the shortest fractional representation). You can add a few exceptions to fine tune such process though (e.g. calculating the p/GCD(p,q) , q/GCD(p,q) and accepting it if one of those are single digit values before proceeding to this method)

Ehsan Foroughi
  • 3,010
  • 2
  • 18
  • 20
0

Pretty dumb solution, just for "previewing" fraction :

factor = 1/decimal
result = 1/Round(factor)
mult = 1

while (result = 1) {
  mult = mult * 10
  result = (1 * mult)/(Round(mult * factor))
}

result = simplify_with_GCD(result)

good luck!

Agnius Vasiliauskas
  • 10,935
  • 5
  • 50
  • 70