首页 > 解决方案 > 如何将小数转换为最接近的分数

问题描述

我想用一个数字将数字的小数转换为最接近的分数!例如,“8.75”应该是“8 3/4”,“1.875”应该是“1 7/8”,但“8,565217...”不应该显示“8 13/23”而是一个错误。Excel中有一个类似的功能解释here

我也想避免使用像 fraction.js 这样的第三个库,而更喜欢使用本机 JS/TS 或 Lodash!有人有想法吗?=)

谢谢您的帮助!

编辑:我尝试了一部分代码,但没有按预期工作 cs 8.75 向我发送 35/4 而不是 8 3/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));
  }

标签: typescriptlodash

解决方案


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

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/5和之间3.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

这对我来说看起来很合理,尽管我不知道你到底想看到哪些边缘情况。无论如何,希望这会有所帮助。祝你好运!


推荐阅读