Преобразование десятичного числа в дробь/рациональное число

Есть ли в JavaScript способ преобразовать десятичное число (например, 0.0002) в дробь, представленную в виде строки (например, «2/10000"»)?

Если бы для этой цели была написана функция с именем decimalToFraction, то decimalToFraction(0.0002) вернула бы строку "2/10000".


person Anderson Green    schedule 09.02.2013    source источник
comment
Этот вопрос имеет очень похожее название, но на самом деле он не является дубликатом этого вопроса (поскольку он касается другой темы). stackoverflow.com/questions/7249195/   -  person Anderson Green    schedule 09.02.2013
comment
Должна ли дробь быть наименьшей (2/10000 == 1/5000)?   -  person Jacob Dalton    schedule 09.02.2013
comment
@JacobDalton Да, было бы полезно преобразовать дробь в самые низкие условия. На Stack Overflow уже есть ответ, который объясняет, как упростить дроби в JavaScript, но я не помню название вопроса.   -  person Anderson Green    schedule 09.02.2013
comment
возможный дубликат преобразовать десятичное число в дробь   -  person Doug Currie    schedule 09.02.2013
comment
@DougCurrie Этот вопрос касается C, а не JavaScript. :/   -  person Anderson Green    schedule 09.02.2013
comment
@Андерсон Грин, ответ на этот вопрос не зависит от языка.   -  person Doug Currie    schedule 09.02.2013
comment
Я бы попытался понять, как работает mindspring.com/~alanh/fracs.html. .   -  person Alex Wayne    schedule 09.02.2013
comment
@Anderson: Код в этом вопросе/ответе не имеет ничего общего с C. Это просто математика. Не сказать, что это дубликат, но не всегда на каком языке написан алгоритм.   -  person Felix Kling    schedule 09.02.2013
comment
@FelixKling Это правда, но ответы на другой вопрос нужно будет вручную преобразовать в JavaScript, чтобы они считались здесь актуальными. Два вопроса не могут считаться дубликатами друг друга, если только они не касаются одной и той же темы.   -  person Anderson Green    schedule 09.02.2013
comment
Лучший алгоритм рациональной аппроксимации Фари с пределом нечеткости: stackoverflow.com/a/43016456/191246   -  person ccpizza    schedule 25.03.2017


Ответы (11)


Вы можете использовать библиотеку fraction.js Эрика Гаррисона, чтобы сделать это и другие дробные операции.

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

Чтобы сделать .003, вы можете просто сделать

var f = new Fraction(.003);
console.log(f.numerator + '/' + f.denominator);
person Trent Earl    schedule 09.02.2013
comment
Какую функцию из Fraction.js следует использовать для преобразования десятичного числа в дробь (например, преобразование 0.003 в дробь, такую ​​как 3/1000)? Я до сих пор не знаю, какую конкретную функцию мне следует использовать для преобразования дробей в десятичные. - person Anderson Green; 09.02.2013
comment
Какие файлы JavaScript я должен включить на веб-страницу, чтобы использовать фракцию.js? В документации это не проясняется. - person Anderson Green; 09.02.2013
comment
@TRENT f = новая фракция (1,67); выдает 167/100, а должно быть 10/6. Как мы можем этого добиться? - person Vasanthan.R.P; 02.04.2014
comment
в Fraction.js для десятичных знаков вы можете использовать var f = new Fraction(.003); f.toString() ‹--- to string будет печатать десятичное число в формате Fraction. Вам не нужно создавать строку дроби самостоятельно - person Ryand.Johnson; 14.04.2015
comment
Остерегайтесь, что Fraction.js, похоже, игнорирует проблемы с плавающей запятой javascript, такие как 19/11 = 1.7272727272727273 (последняя цифра), которые Fraction.js затем форматирует в 1 727272727/1000000000. - person Basti; 09.06.2015

Немного погуглил термин "десятичная дробь js" первый дал это:

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

Кажется, это работает:

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"
person Alex Wayne    schedule 09.02.2013
comment
Хм. Попробуйте преобразовать 2,56 в дробь с этим. Должно быть 2 и 14/25 - person Popnoodles; 09.02.2013
comment
Хе, да. Бьюсь об заклад, ошибка округления с плавающей запятой делает это странным. - person Alex Wayne; 09.02.2013
comment
Когда я проверил число 2.56 в качестве входных данных, я получил следующий вывод, который явно неверен: 2 7881299347898369/14073748835532800. Я постараюсь найти лучшее решение, если это возможно. - person Anderson Green; 09.02.2013
comment
После некоторого тестирования строка, умножающая десятичную часть на целое число, имеет ошибку с плавающей запятой, приводящую к 56,0000000001. Решение, которое я нашел, состояло в том, чтобы просто использовать substr(2,decimal.length) в десятичной строке, которая дает правильный ответ (во всяком случае, в моем коде) - person robjtede; 31.12.2013
comment
К сожалению, ссылка в ответе уже недействительна. - person Jarrett; 12.02.2015
comment
Вот улучшенная версия этой скрипты: jsfiddle.net/matikucharski/x4a8ntj0, которая правильно вычисляет 2,56. - person MatiK; 03.06.2016

Я использовал этот сайт http://mathforum.org/library/drmath/view/51886.html для создания функции, но, как упоминается в статье, вы получите необоснованно большое число для радикалов или пи.

Надеюсь, это поможет.

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

Если вам просто нужна автономная функция, которая возвращает только числитель и знаменатель, используйте функцию ниже.

var toFraction = function (dec) {
    var is_neg = dec < 0;
    dec = Math.abs(dec);
    var done = false;
    //you can adjust the epsilon to a larger number if you don't need very high precision
    var n1 = 0, d1 = 1, n2 = 1, d2 = 0, n = 0, q = dec, epsilon = 1e-13;
    while (!done) {
        n++;
        if (n > 10000) {
            done = true;
        }
        var a = parseInt(q);
        var num = n1 + a * n2;
        var den = d1 + a * d2;
        var e = (q - a);
        if (e < epsilon) {
            done = true;
        }
        q = 1 / e;
        n1 = n2;
        d1 = d2;
        n2 = num;
        d2 = den;
        if (Math.abs(num / den - dec) < epsilon || n > 30) {
            done = true;
        }
    }
    return [is_neg ? -num : num, den];
};
//Usage:
//var frac = toFraction(0.5);
//console.log(frac)
//Output: [ 1, 2 ]
person jiggzson    schedule 04.03.2013
comment
Этот работает лучше, так как он определяет 1,3333 как 1 1/3 и т. д. - person Dex; 29.08.2017

Очень старый вопрос, но, возможно, кто-то найдет это полезным. Он итеративный, а не рекурсивный и не требует факторизации.

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};
};
person Walter    schedule 28.05.2014

Существует очень простое решение, использующее строковое представление чисел.

    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));

который просто проверит, сколько чисел содержится в дроби, а затем расширит дробь f/1, пока f не станет целым числом. Это может привести к огромным дробям, поэтому вы можете уменьшить их, разделив числитель и знаменатель на наибольший общий делитель обоих, например.

    // 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;
    }
person Chemistree    schedule 09.03.2016

Хорошая новость заключается в том, что это возможно, но вам придется преобразовать его в код.

Давайте пойдем с 2,56 без всякой причины.

Используйте десятичную часть числа .56

В .56 2 цифры, запишите .56 как 56/100.

Итак, у нас есть 2 + 56/100, и нам нужно сократить эту дробь до наименьших членов, разделив числитель и знаменатель на наибольший общий делитель, который в данном случае равен 4.

Таким образом, эта дробь, приведенная к низшим частям, равна 2 + 14/25.

Чтобы сложить эти целых 2, мы умножаем на делитель и добавляем к 14

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

person Popnoodles    schedule 09.02.2013
comment
И 2*25+14; тогда 64/25 :) - person Janaka R Rajapaksha; 11.05.2014

Пробовали что-то подобное?

<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>
person Janaka R Rajapaksha    schedule 11.05.2014

Я сделал то, что предложил popnoodles, и вот оно

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 "";
  }
}
person hewstone    schedule 08.07.2014

Это может быть немного старым, но опубликованный код не работает при 0 значениях. Я исправил эту ошибку и опубликую обновленный код ниже.

//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;
}
}
person RWolfe    schedule 10.07.2015

Я просто хочу оставить одну альтернативу, которую я нашел для преобразования десятичных чисел в дроби и сокращения дробей, это библиотека JS.

Библиотека вызывает fraction.js, это было очень полезно для меня и сэкономило мне много времени и Работа. Надежда может быть полезна кому-то еще!

person Jabel Márquez    schedule 21.10.2014

Я знаю, что это старый вопрос, но я создал функцию, которая была значительно упрощена.

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;
}

Назовите это с помощью Math.fraction(2.56)

Будет:

  • вернуть NaN, если ввод не является числом
  • вернуть undefined, если ввод не определен
  • уменьшить дробь
  • вернуть string (используйте Math.fraction(2.56).split("/") для массива, содержащего числитель и знаменатель)

Обратите внимание, что здесь используется устаревший arguments.callee, поэтому он может быть несовместим с некоторыми браузерами.

Проверьте это здесь

person Samuel Williams    schedule 15.11.2017