如何将小数转换为最接近的分数

时间:2019-04-03 12:36:52

标签: typescript lodash

我想将数字的十进制转换为一个数字最接近的分数! 例如,“ 8.75”应为“ 8 3/4”,“ 1.875”应为“ 1 7/8”,但“ 8,565217 ...”不应显示“ 8 13/23”,而是错误。 Excel here

中有类似的功能

我也想避免使用第三个lib之类的fraction.js,而是更喜欢使用本机JS / TS或Lodash!有人有主意吗? =)

谢谢您的帮助!

编辑: 我尝试了一部分代码,但无法正常工作。CS 8.75给我发送了35/4,而没有发送给我83/4 ......

  private checkNumberToCompute(numberToCompute: any) {
    let numberToReturn = numberToCompute;
    if (
      (!isNaN(Number(numberToCompute)) && numberToCompute.includes('.')) ||
      (numberToCompute.includes(',') && !numberToCompute.includes('/'))
    ) {
      console.log('Nombre à décimal sans fraction');
      numberToReturn = this.computeFractions(numberToCompute);
    }
    return numberToReturn;
  }
  private computeFractions(numberToCompute: any): string {
    console.log('numberToCompute', numberToCompute);

    const lenghtOfDecimals = numberToCompute.substring(numberToCompute.indexOf('.') + 1).length;
    let denominator = Math.pow(10, lenghtOfDecimals),
      numerator = numberToCompute * denominator;
    const divisor = this.gcd(numerator, denominator);

    numerator /= divisor;
    denominator /= divisor;
    return Math.floor(numerator) + '/' + Math.floor(denominator);
  }

  private gcd(numerator: number, denominator: number): any {
    if (denominator < 0.0000001) {
      return numerator;
    }
    return this.gcd(denominator, Math.floor(numerator % denominator));
  }

1 个答案:

答案 0 :(得分:3)

好吧,我不确定这是否正是您想要的,但希望它能给您一些有关如何进行的想法:

const acceptableDenominators = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const maxDistanceToNumerator = 0.0001;

function numberToFractionString(n: number): string | undefined {
    const negative = (n < 0);
    if (negative) n = -n;

    const wholePart = Math.floor(n);
    n -= wholePart;

    const denom = acceptableDenominators.find(d =>
        Math.abs(d * n - Math.round(d * n)) <= maxDistanceToNumerator
    );
    if (typeof denom === 'undefined') {
        return;
    }
    const numer = Math.round(denom * n);

    if (denom === 1) {
        return "" + (wholePart + numer) * (negative ? -1 : 1);
    }

    return (negative ? "-" : "") +
        (wholePart ? wholePart + " " : "") +
        numer + "/" + denom;

}

这个想法是,您需要弄清楚分数的可接受的分母是什么;在您的情况下,您似乎只需要一个数字,所以这就是我仅指定1-9的原因。另外,您需要弄清楚浮点数必须与小数之间的接近程度才能接受它。在这种情况下,我已指定要被识别为3/5的事物,它必须在2.9999/53.0001/5之间。

然后有很多边缘情况要处理(负数和非常接近整数的数字很有趣),但是主要步骤是只检查每个可能的分母从最低到最高(自动给您减少的分数,因为它将在4/8之前找到1/2)并选择第一个分子足以接近整数的分子...或返回undefined(而不是抛出错误,但是如果您想要的话)。

让我们看看它是否有效:

const tests = [8.75, 1.875, 8.565217, 9.99999999, -1, -0.888889,
    0, 1e140, -1e-140, -0.111111, 0.5,
    -7.66667, -7.6667, -7.667, -7.67, -7.7,
    NaN, Infinity, -Infinity];

tests.forEach(n =>
    console.log("" + n + ": " + String(numberToFractionString(n)))
);

// 8.75: 8 3/4
// 1.875: 1 7/8
// 8.565217: undefined
// 9.99999999: 10
// -1: -1
// -0.888889: -8/9
// 0: 0
// 1e+140: 1e+140
// -1e-140: 0
// -0.111111: -1/9
// 0.5: 1/2
// -7.66667: -7 2/3
// -7.6667: -7 2/3
// -7.667: undefined
// -7.67: undefined
// -7.7: undefined
// NaN: undefined
// Infinity: undefined
// -Infinity: undefined

在我看来,这是合理的,尽管我不知道您对于某些极端情况到底希望看到什么。无论如何,希望能有所帮助。祝你好运!