17

So i want to be able to convert any decimal number into fraction. In both forms such as one without remainder like this: 3/5 or with remainder: 3 1/4.

what i was doing is this..

lets say i have number .3435.

  • Calculate amount of digits after decimals.
  • multiply by 10 with power of the amount before number.
  • then somehow find greatest common factor.

Now i don't know how to find GCF. And nor i know how to implement logic to find fraction that represents a number closely or in remainder form if exact fraction doesn't exists.

code i have so far: (testing)

x = 34/35;
a = x - x.toFixed();
tens = (10).pow(a.toString().length - 2);

numerator = tens * x;
denominator = tens;
Muhammad Umer
  • 17,263
  • 19
  • 97
  • 168

12 Answers12

29

Your first 2 steps are reasonable.

But what you should do is for the numerator and denominator calculate the Greatest Common Divisor (GCD) and then divide the numerator and denominator with that divisor to get the fraction you want.

GCD is rather easy to calculate. Here is Euclid's algorithm:

var gcd = function(a, b) {
  if (!b) return a;

  return gcd(b, a % b);
};

Edit

I've added a fully working JSFiddle.

mega6382
  • 9,211
  • 17
  • 48
  • 69
Sani Huttunen
  • 23,620
  • 6
  • 72
  • 79
  • What do you get with 0.3435? – Xotic750 May 09 '14 at 23:45
  • @Xotic750: Obviously there needs to be a limit check since the nature of fractions. Something like `if (b < 0.0000001) return a;` but the theory is still sound. – Sani Huttunen May 10 '14 at 00:03
  • I updated his fiddle with some stuff that gives correct answer http://jsfiddle.net/5QrhQ/3/. – Muhammad Umer May 10 '14 at 00:06
  • @SaniHuttunen I wasn't knocking the theory or your answer, I was just pointing out the fact that there are other things to consider, and using someone else's efforts may be a more effective solution, unless you are doing this as some kind of learning. :) – Xotic750 May 10 '14 at 00:09
  • what do you think of it now? – Muhammad Umer May 10 '14 at 00:11
  • @Xotic750: Yes. You should always try not to reinvent the wheel but as you pointed out this is probably for learning. – Sani Huttunen May 10 '14 at 00:12
  • @MuhammadUmer: `parseInt` is one way to go. Another is [`Math.floor`](http://jsfiddle.net/5QrhQ/5/). – Sani Huttunen May 10 '14 at 00:13
  • @SaniSinghHuttunen Answer is Simple, Aesthetic and Perfect –  May 27 '18 at 06:03
  • Doesn't work as well with negative decimals. For example fraction of -3.00001 results in -4/1 and of -3.0000 in -3/1. – Pawel Kam Mar 25 '20 at 18:38
  • 2/6 returns something weired either: 333333333333333/1000000000000000 - due to insanitized digital representation.... – Xerix Dec 18 '20 at 06:08
15

Unless you are willing to work on developing something yourself then I would suggest using a library that someone has already put effort into, like fraction.js

Javascript

var frac = new Fraction(0.3435);

console.log(frac.toString());

Output

687/2000

On jsFiddle

Xotic750
  • 22,914
  • 8
  • 57
  • 79
13

You can use brute force test on different denominators and retain the result that has least error.

The algorithm below is an example of how you might go about this, but, suffers from being inefficient and limited to searching for denominators up to 10000.

function find_rational( value, maxdenom ) {
  console.clear();
  console.log( "Looking up: " + value );
  let best = { numerator: 1, denominator: 1, error: Math.abs(value - 1) }
  if ( !maxdenom ) maxdenom = 10000;
  for ( let denominator = 1; best.error > 0 && denominator <= maxdenom; denominator++ ) {
    let numerator = Math.round( value * denominator );
    let error = Math.abs( value - numerator / denominator );
    if ( error >= best.error ) continue;
    best.numerator = numerator;
    best.denominator = denominator;
    best.error = error;
    console.log( "Intermediate result: "
                   + best.numerator + "/" + best.denominator
                   + " (" + ( best.numerator/best.denominator)
                   + " error " + best.error + " )" );
  }
  console.log( "Final result: " + JSON.stringify( best ) );
  return best;
}
  
function calc() {
    const value = parseFloat( $("#myInput").val() );
    if ( isNaN(value) ) {
        $( "#myResult" ).val( "NaN" );
        return;
    }
    const rational = find_rational( value, 10000 );
    $("#myResult").val( rational.numerator
                        + " / " + rational.denominator
                        + " ( Error: " + rational.error + " )" );
}

calc();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<P>
Enter a decimal number:<BR/>
<INPUT type="text" name="myInput" id="myInput" value=".3435" onkeyup="calc()"/><BR/>
</P>

<P>
Resulting Rational:<BR/>
<INPUT name="myResult" id="myResult" value=""/><BR/>
</P>

The above determines the .3435 as a fraction is 687 / 2000.

Also, had you gave it PI (e.g. 3.1415926) it produces good looking fractions like 22/7 and 355/113.

Stephen Quan
  • 21,481
  • 4
  • 88
  • 75
10

I get very poor results using the GCD approach. I got much better results using an iterative approach.

For example, here is a very crude approach that zeros in on a fraction from a decimal:

function toFraction(x, tolerance) {
    if (x == 0) return [0, 1];
    if (x < 0) x = -x;
    if (!tolerance) tolerance = 0.0001;
    var num = 1, den = 1;

    function iterate() {
        var R = num/den;
        if (Math.abs((R-x)/x) < tolerance) return;

        if (R < x) num++;
        else den++;
        iterate();
    }

    iterate();
    return [num, den];
}

The idea is you increment the numerator if you are below the value, and increment the denominator if you are above the value.

chowey
  • 9,138
  • 6
  • 54
  • 84
10

One quick and easy way of doing it is

getFraction = (decimal) => {
  for(var denominator = 1; (decimal * denominator) % 1 !== 0; denominator++);
  return {numerator: decimal * denominator, denominator: denominator};
}
rman
  • 135
  • 1
  • 7
  • `getFraction(0.003076991306616206)` takes over 10 seconds, but it works well enough – bryc Jun 20 '20 at 17:18
5

Use the Euclidean algorithm to find the greatest common divisor.

function reduce(numerator,denominator){
  var gcd = function gcd(a,b){
    return b ? gcd(b, a%b) : a;
  };
  gcd = gcd(numerator,denominator);
  return [numerator/gcd, denominator/gcd];
}

This will provide you with the following results on your console

reduce(2,4);
// [1,2]

reduce(13427,3413358);
// [463,117702]

So by continuing from already what you have,

var x = 34/35;
var a = x - x.toFixed();
var tens = Math.pow(10,a.toString().length - 2);

var numerator = tens * x;
var denominator = tens;

reduce(numerator,denominator);

Source: https://stackoverflow.com/a/4652513/1998725

Dehan
  • 4,818
  • 1
  • 27
  • 38
3

I had researched all over the website and I did combine all code into one, Here you go!

function fra_to_dec(num){
    var test=(String(num).split('.')[1] || []).length;
    var num=(num*(10**Number(test)))
    var den=(10**Number(test))
    function reduce(numerator,denominator){
        var gcd = function gcd(a,b) {
            return b ? gcd(b, a%b) : a;
        };
        gcd = gcd(numerator,denominator);
        return [numerator/gcd, denominator/gcd];
    }
    return (reduce(num,den)[0]+"/"+reduce(num,den)[1])
}

This code is very easy to use! You can even put number in this function!

N'Bayramberdiyev
  • 5,936
  • 7
  • 27
  • 47
2

The tricky bit is not letting floating points get carried away.

Converting a number to a string restrains the trailing digits,

especially when you have a decimal with an integer, like 1.0625.

You can round off clumsy fractions, by passing a precision parameter.

Often you want to force a rounded value up, so a third parameter can specify that.

(e.g.; If you are using a precision of 1/64, the smallest return for a non-zero number will be 1/64, and not 0.)

Math.gcd= function(a, b){
    if(b) return Math.gcd(b, a%b);
    return Math.abs(a);
}
Math.fraction= function(n, prec, up){
    var s= String(n), 
    p= s.indexOf('.');
    if(p== -1) return s;

    var i= Math.floor(n) || '', 
    dec= s.substring(p), 
    m= prec || Math.pow(10, dec.length-1), 
    num= up=== 1? Math.ceil(dec*m): Math.round(dec*m), 
    den= m, 
    g= Math.gcd(num, den);

    if(den/g==1) return String(i+(num/g));

    if(i) i= i+' and  ';
    return i+ String(num/g)+'/'+String(den/g);
}

Math.roundFraction(.3435,64); value: (String) 11/32

kennebec
  • 102,654
  • 32
  • 106
  • 127
2

Inspired by @chowey answer, which contained recursive implementation of finding close fraction for a decimal value within given tolerance, here is better (see benchmark), iterative version of it.

function toFractionIterative(x, epsilon = 0.0001) {
    if (x == 0) return [0, 1];
    const a = Math.abs(x);
    let n = 0;
    let d = 1;
    let r;
    while (true) {
        r = n / d;
        if (Math.abs((r - a) / a) < epsilon) {
            break;
        }
        if (r < a) {
            n++;
        }
        else {
            d++;
        }
    }
    return [x < 0 ? -n : n, d];
}

Benchmark (tl;dr: recursive 1,589 ops/s, iterative 5,955 ops/s; use iterative approach)

AgainPsychoX
  • 1,527
  • 1
  • 16
  • 20
1
let v = 3.141592;
document.write(d2f(v)); // 392699/125000
function d2f(v) // decimal to fraction
{
    if (Math.floor(v) == v) return v + '/' + 1;
    v = Math.abs(v);
    let ret = .01,            // rounding error tolerance
        td = v-Math.floor(v), // trailing digits
        r = 1/td,             // reciprocal
        d = r,                // start building denominator
        lim = 20;             // max loop limit
    for (let i = 0; i < lim; i++)
    {
        td = r-Math.floor(r);
        if (Math.abs(r-Math.round(r)) < ret) break;
        r = 1/td;
        d *= r;
    }
    return Math.round(d*v) + '/' + Math.round(d);
}
0

I came up with this for 16ths

function getfract(theNum){
    var input=theNum.toString();
    var whole = input.split(".")[0];
    var rem = input.split(".")[1] * .1;
    return(whole + " " + Math.round(rem * 16) + "/16");
}
Jeff Price
  • 57
  • 1
0
function decimalToFraction(num) {
    let numsAfterDecPoint = num.toString().split('.')[1] ? num.toString().split('.')[1].length : 0;

    let numerator = num * Math.pow(10, numsAfterDecPoint);
    let denominator = Math.pow(10, numsAfterDecPoint);

    console.log(numerator + " / " + denominator)

    let d = GCD(numerator,denominator)

    return numerator / d + " / " + denominator / d

}

console.log(decimalToFraction(0.5)); // 5 / 10 => 1 / 2

console.log(decimalToFraction(178.45)); // 17845 / 100 => 3569 / 20

function GCD(a,b) {
    let r = 0;

    while(b != 0) {
        r = a % b 
        a = b;
        b = r;
    }

    return a;
}
Dula
  • 1,276
  • 5
  • 14
  • 23
Talal
  • 1
  • 1