Convert a decimal number to a fraction / rational

2019-01-11 18:36发布

问题:

In JavaScript, is there any way to convert a decimal number (such as 0.0002) to a fraction represented as a string (such as "2/10000")?

If a function called decimalToFraction had been written for this purpose, then decimalToFraction(0.0002) would return the string "2/10000".

回答1:

You can use Erik Garrison's fraction.js library to do that and more fractional operations.

var f = new Fraction(2, 10000);
console.log(f.numerator + '/' + f.denominator);

To to do .003 you can just do

var f = new Fraction(.003);
console.log(f.numerator + '/' + f.denominator);


回答2:

A little googling with the term "decimal to fraction js" the first yielded this:

http://wildreason.com/wildreason-blog/2010/javascript-convert-a-decimal-into-a-simplified-fraction/

It seems to work:

http://jsfiddle.net/VKfHH/

function HCF(u, v) { 
    var U = u, V = v
    while (true) {
        if (!(U%=V)) return V
        if (!(V%=U)) return U 
    } 
}
//convert a decimal into a fraction
function fraction(decimal){

    if(!decimal){
        decimal=this;
    }
    whole = String(decimal).split('.')[0];
    decimal = parseFloat("."+String(decimal).split('.')[1]);
    num = "1";
    for(z=0; z<String(decimal).length-2; z++){
        num += "0";
    }
    decimal = decimal*num;
    num = parseInt(num);
    for(z=2; z<decimal+1; z++){
        if(decimal%z==0 && num%z==0){
            decimal = decimal/z;
            num = num/z;
            z=2;
        }
    }
    //if format of fraction is xx/xxx
    if (decimal.toString().length == 2 && 
            num.toString().length == 3) {
                //reduce by removing trailing 0's
        decimal = Math.round(Math.round(decimal)/10);
        num = Math.round(Math.round(num)/10);
    }
    //if format of fraction is xx/xx
    else if (decimal.toString().length == 2 && 
            num.toString().length == 2) {
        decimal = Math.round(decimal/10);
        num = Math.round(num/10);
    }
    //get highest common factor to simplify
    var t = HCF(decimal, num);

    //return the fraction after simplifying it
    return ((whole==0)?"" : whole+" ")+decimal/t+"/"+num/t;
}

// Test it
alert(fraction(0.0002)); // "1/5000"


回答3:

I used this site http://mathforum.org/library/drmath/view/51886.html to build a function but as the article mentions you will get an unreasonable large number for radicals or pi.

Hope it helps though.

function Fraction(){}
Fraction.prototype.convert = function(x, improper)
{
    improper = improper || false;
    var abs = Math.abs(x);
    this.sign = x/abs;
    x = abs;
    var stack = 0;
    this.whole = !improper ? Math.floor(x) : 0;
    var fractional = !improper ? x-this.whole : abs;
    /*recursive function that transforms the fraction*/
    function recurs(x){
        stack++;
        var intgr = Math.floor(x); //get the integer part of the number
        var dec = (x - intgr); //get the decimal part of the number
        if(dec < 0.0019 || stack > 20) return [intgr,1]; //return the last integer you divided by
        var num = recurs(1/dec); //call the function again with the inverted decimal part
        return[intgr*num[0]+num[1],num[0]]
    }
    var t = recurs(fractional); 
    this.numerator = t[0];
    this.denominator = t[1];
}

Fraction.prototype.toString = function()
{
    var l  = this.sign.toString().length;
    var sign = l === 2 ? '-' : '';
    var whole = this.whole !== 0 ? this.sign*this.whole+' ': sign;
    return whole+this.numerator+'/'+this.denominator;
}

//var frac = new Fraction()
//frac.convert(2.56, false)
//console.log(frac.toString())
//use frac.convert(2.56,true) to get it as an improper fraction


回答4:

Very old question but maybe someone can find this useful. It's iterative, not recursive and doesn't require factorization

function getClosestFraction(value, tol) {
    var original_value = value;
    var iteration = 0;
    var denominator=1, last_d = 0, numerator;
    while (iteration < 20) {
        value = 1 / (value - Math.floor(value))
        var _d = denominator;
        denominator = Math.floor(denominator * value + last_d);
        last_d = _d;
        numerator = Math.ceil(original_value * denominator)

        if (Math.abs(numerator/denominator - original_value) < tol)
            break;
        iteration++;
    }
    return {numerator: numerator, denominator: denominator};
};


回答5:

There is a very simple solution using string representation of numbers

    string = function(f){ // returns string representation of an object or number
        return f+"";
    }
    fPart = function(f){ // returns the fraction part (the part after the '.') of a number
        str = string(f);
        return str.indexOf(".")<0?"0":str.substring(str.indexOf(".") + 1);
    }
    wPart = function(f){ // returns the integer part (the part before the '.') of a number
        str = string(f);
        return str.indexOf(".")<0?str:str.substring(0, str.indexOf(".")); // possibility 1
        //return string(f - parseInt(fPart(f))); // just substract the fPart
    }

    power = function(base, exp){
        var tmp = base;
        while(exp>1){
            base*=tmp;
            --exp;
        }
        return base;
    }

    getFraction = function(f){ // the function
        var denominator = power(10, fPart(f).length), numerator = parseInt(fPart(f)) + parseInt(wPart(f))*denominator;
        return "[ " + numerator + ", " + denominator + "]";
    }

    console.log(getFraction(987.23));

which will just check how many numbers are in the fraction and then expands the fraction of f/1 until f is an integer. This can lead to huge fractions, so you can reduce it by dividing both numerator and denominator by the greatest common divisor of both, e.g.

    // greatest common divisor brute force
    gcd = function(x,y){
        for(var i = Math.min(x, y);i>0;i--) if(!(x%i||y%i)) return i;
        return 1;
    }


回答6:

The good news is that it's possible, but you'll have to convert it to code.

Let's go with 2.56 for no reason at all.

Use the decimal portion of the number .56

There are 2 digits in .56, write .56 as 56/100.

So we have 2 + 56/100 and need to reduce this fraction to lowest terms by dividing both the numerator and denominator by the greatest common divisor, which is 4 in this case.

So, this fraction reduced to lowest terms is 2 + 14/25.

To add those whole 2, we multiply by the divisor and add to the 14

(2*25 + 14)/25 = 64/25



回答7:

Have tried something like this?

<script type="texrt/javascript>
var cnum = 3.5,deno = 10000,neww;
neww = cnum * deno;
while(!(neww % 2 > 0) && !(deno % 2 > 0)){
    neww = neww / 2;
    deno = deno / 2;
}
while(!(neww % 3 > 0) && !(deno % 3 > 0)){
    neww = neww / 3;
    deno = deno / 3;
}
while(!(neww % 5 > 0) && !(deno % 5 > 0)){
    neww = neww / 5;
    deno = deno / 5;
}
while(!(neww % 7 > 0) && !(deno % 7 > 0)){
    neww = neww / 7;
    deno = deno / 7;
}
while(!(neww % 11 > 0) && !(deno % 11 > 0)){
    neww = neww / 11;
    deno = deno / 11;
}
while(!(neww % 13 > 0) && !(deno % 13 > 0)){
    neww = neww / 13;
    deno = deno / 13;
}
while(!(neww % 17 > 0) && !(deno % 17 > 0)){
    neww = neww / 17;
    deno = deno / 17;
}
while(!(neww % 19 > 0) && !(deno % 19 > 0)){
    neww = neww / 19;
    deno = deno / 19;
}
console.log(neww+"/"+deno);
</script>


回答8:

I did what popnoodles suggested and here it is

function FractionFormatter(value) {
  if (value == undefined || value == null || isNaN(value))
    return "";

  function _FractionFormatterHighestCommonFactor(u, v) {
      var U = u, V = v
      while (true) {
        if (!(U %= V)) return V
        if (!(V %= U)) return U
      }
  }

  var parts = value.toString().split('.');
  if (parts.length == 1)
    return parts;
  else if (parts.length == 2) {
    var wholeNum = parts[0];
    var decimal = parts[1];
    var denom = Math.pow(10, decimal.length);
    var factor = _FractionFormatterHighestCommonFactor(decimal, denom)
    return (wholeNum == '0' ? '' : (wholeNum + " ")) + (decimal / factor) + '/' + (denom / factor);
  } else {
    return "";
  }
}


回答9:

This may be a little old but the code that was posted fails on 0 values I have fixed that error and will post the updated code below

//function to get highest common factor of two numbers (a fraction)
function HCF(u, v) { 
    var U = u, V = v
    while (true) {
        if (!(U%=V)) return V
        if (!(V%=U)) return U 
    } 
}
//convert a decimal into a fraction
function fraction(decimal){

    if(!decimal){
        decimal=this;
    }
    whole = String(decimal).split('.')[0];
    decimal = parseFloat("."+String(decimal).split('.')[1]);
    num = "1";
    for(z=0; z<String(decimal).length-2; z++){
        num += "0";
    }
    decimal = decimal*num;
    num = parseInt(num);
    for(z=2; z<decimal+1; z++){
        if(decimal%z==0 && num%z==0){
            decimal = decimal/z;
            num = num/z;
            z=2;
        }
    }
    //if format of fraction is xx/xxx
    if (decimal.toString().length == 2 && 
        num.toString().length == 3) {
            //reduce by removing trailing 0's
            // '
    decimal = Math.round(Math.round(decimal)/10);
    num = Math.round(Math.round(num)/10);
}
//if format of fraction is xx/xx
else if (decimal.toString().length == 2 && 
        num.toString().length == 2) {
    decimal = Math.round(decimal/10);
    num = Math.round(num/10);
}
//get highest common factor to simplify
var t = HCF(decimal, num);

//return the fraction after simplifying it

if(isNaN(whole) === true)
{
 whole = "0";
}

if(isNaN(decimal) === true)
{
    return ((whole==0)?"0" : whole);
}
else
{
    return ((whole==0)?"0 " : whole+" ")+decimal/t+"/"+num/t;
}
}


回答10:

I just want a leave one alternative that I found to convert decimal numbers into fractions and reducing fractions, it's a JS library.

The library calls fraction.js, it was really helpful for me and save me a lot time and work. Hope can be useful to somebody else!



回答11:

I know this is an old question, but I have created a function that has been greatly simplified.

Math.fraction=function(x){
return x?+x?x.toString().includes(".")?x.toString().replace(".","")/(function(a,b){return b?arguments.callee(b,a%b):a;})(x.toString().replace(".",""),"1"+"0".repeat(x.toString().split(".")[1].length))+"/"+("1"+"0".repeat(x.toString().split(".")[1].length))/(function(a,b){return b?arguments.callee(b,a%b):a;})(x.toString().replace(".",""),"1"+"0".repeat(x.toString().split(".")[1].length)):x+"/1":NaN:void 0;
}

Call it with Math.fraction(2.56)

It will:

  • return NaN if the input is not a number
  • return undefined if the input is undefined
  • reduce the fraction
  • return a string (use Math.fraction(2.56).split("/") for an array containing the numerator and denominator)

Please note that this uses the deprecated arguments.callee, and thus may be incompatible in some browsers.

Test it here