繁体   English   中英

"将十进制数转换为javascript中的分数或最接近的分数"

[英]convert decimal number to fraction in javascript or closest fraction

所以我希望能够将任何十进制数转换为分数。 在这两种形式中,例如没有余数的一种: 3\/5<\/code>或有余数: 3 1\/4<\/code> 。

我正在做的是这个..

假设我有数字 .3435。

  • 计算小数点后的位数。<\/li>
  • 乘以 10 乘以数字前的数量的幂。<\/li>
  • 然后以某种方式<\/strong>找到最大公因数。<\/li><\/ul>

    现在我不知道如何找到 GCF。 而且我也不知道如何实现逻辑来查找紧密表示数字的分数或在不存在精确分数的情况下以余数形式表示的分数。

    到目前为止我的代码:(测试)

您的前两个步骤是合理的。

但是您应该做的是为分子和分母计算最大公约数(GCD) ,然后将分子和分母与该除数相除以获得您想要的分数。

GCD 相当容易计算。 这是欧几里德的算法

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

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

编辑

我添加了一个完全可用的 JSFiddle

除非你愿意自己开发一些东西,否则我建议使用一个已经有人努力开发的库,比如fraction.js

Javascript

var frac = new Fraction(0.3435);

console.log(frac.toString());

输出

687/2000

jsFiddle 上

您可以对不同的分母使用蛮力测试并保留误差最小的结果。

下面的算法是一个示例,说明您可能如何进行此操作,但效率低下且仅限于搜索最多 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>

以上确定 0.3435 作为一个分数是 687 / 2000。

此外,如果你给它 PI(例如 3.1415926),它会产生漂亮的分数,如 22/7 和 355/113。

一种快速简便的方法是

getFraction = (decimal) => {
  for(var denominator = 1; (decimal * denominator) % 1 !== 0; denominator++);
  return {numerator: decimal * denominator, denominator: denominator};
}

使用欧几里得算法找到最大公约数。

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

这将在您的控制台上为您提供以下结果

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

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

所以通过继续你已经拥有的,

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

来源: https : //stackoverflow.com/a/4652513/1998725

使用 GCD 方法我得到的结果很差。 我使用迭代方法得到了更好的结果。

例如,这是一种非常粗略的方法,它对小数的分数进行归零:

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

这个想法是,如果您低于该值,则增加分子,如果高于该值,则增加分母。

棘手的一点是不要让浮点被带走。

将数字转换为字符串会限制尾随数字,

特别是当你有一个带整数的小数时,比如 1.0625。

您可以通过传递一个precision参数来舍入笨拙的分数。

通常你想强制一个向上舍入的值,所以第三个参数可以指定。

(例如;如果您使用 1/64 的精度,则非零数的最小返回值为 1/64,而不是 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); 值:(字符串)11/32

我已经研究了整个网站,我确实将所有代码合二为一,你去吧!

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])
}

这段代码非常好用! 你甚至可以把这个功能!

我想出了这个 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");
}

这是我最快的一次。 没有 GCD。 适用于至少 5 个尾随小数。

function decToFrac(n)
{
    let rcp = (n%1 == 0) ? 1: 1/(n%1);
    let den = rcp;
    const lim = 10;
    for (let i = 0; i < lim && Number.isInteger(Math.round(rcp * 10**(lim-i)) / 10**(lim-i)) != true; i++)
    {
        rcp = 1/(rcp%1);
        den *= rcp;
    }
    return [Math.round(n*den), Math.round(den)];
}

function impToProp(n, d)
{
    return [Math.floor(n/d), (n%d), d];
}

var dec = 3.141592;
var frac = decToFrac(dec); // improper
var prop = impToProp(frac[0], frac[1]); // proper

受到@chowey 答案的启发,其中包含在给定容差内找到十进制值的近似分数的递归实现,这里是更好的(参见基准),它的迭代版本。

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

基准测试(tl;dr:递归 1,589 ops/s,迭代 5,955 ops/s;使用迭代方法)

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

}

控制台日志(decimalToFraction(0.5)); // 5 / 10 => 1 / 2

控制台日志(decimalToFraction(178.45)); // 17845 / 100 => 3569 / 20

函数 GCD(a,b) { 让 r = 0;

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

}

只有当您没有重复的小数或不需要提及无理数时,GCD 解决方案才适用。 当然,除了近似值之外,对于非理性我们无能为力,但是对于重复的,只要 JS 精度允许,我们仍然可以做一些事情。

大多数情况下,您的分数会重复,例如1\/112<\/code> ,即0.008928571428571428<\/code> 。 正如您在0089<\/code> 285714<\/code>之后可能注意到的那样,它是重复的。

算法:<\/h3>
    为了回到 GCD 方法,我们必须去掉重复部分。 所以让我们考虑上面的例子。 它每 6 位数字重复一次( 285714<\/code> )。 因此,如果我们将0.008928571428571428<\/code> (我们称之为x<\/code> )乘以10^6<\/code> ,那么我们就有这样一个数字,即重复部分全部向左移动 6 位。 所以简而言之, 1000000x = 8928.571428571428<\/code><\/li>
  1. 现在1000000x - x = 999999x<\/code>是一个数字,它根本没有重复,因为重复序列只需要取消。 因此,当您执行此操作时,您最终会得到999999x = 8928.562499999998<\/code> 。 当浮点错误被忽略时,这当然应该是8928.5625<\/code> 。 我们可能会尝试通过parseFloat(z.toPrecision(15))<\/code>来修复它,或者您可能会想出一些其他巧妙的方法。 但是让我们同意 999999x 是8928.5625<\/code> 。<\/li>
  2. 现在我们需要做的就是找出小数点后的位数。 同样,您可能会想出一些好主意,但至于热度,让我们像(8928.5625 % 1).toString().length - 2<\/code>那样做,结果是4<\/code> 。<\/li>
  3. 我们现在非常接近我们的答案。 让我们将两边乘以10 **4<\/code>得到9999990000x = 89285625<\/code> 。 x = 89285625 \/ 9999990000<\/code> 。 现在应用GCD 函数<\/a>,结果表明89285625<\/code>实际上是 GCD,所以x = 1\/112<\/code> 。<\/li><\/ol>"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM