【问题标题】:broken toFixed implementation [duplicate]损坏的toFixed实现[重复]
【发布时间】:2011-03-30 18:42:26
【问题描述】:

javascript 的“Number.toFixed”的默认实现似乎有点损坏。

console.log((8.555).toFixed(2));    // returns 8.56
console.log((8.565).toFixed(2));    // returns 8.57
console.log((8.575).toFixed(2));    // returns 8.57
console.log((8.585).toFixed(2));    // returns 8.59

我需要一种比这更一致的舍入方法。

在 8.500 和 8.660 之间的范围内,以下数字无法正确四舍五入。

8.575
8.635
8.645
8.655

我已尝试按如下方式修复原型实现,但这只是完成了一半。任何人都可以提出任何可以使其更一致地工作的更改吗?

Number.prototype.toFixed = function(decimalPlaces) {
    var factor = Math.pow(10, decimalPlaces || 0);
    var v = (Math.round(this * factor) / factor).toString();
    if (v.indexOf('.') >= 0) {
        return v + factor.toString().substr(v.length - v.indexOf('.'));
    }
    return v + '.' + factor.toString().substr(1);
};

【问题讨论】:

标签: javascript


【解决方案1】:

这是因为浮点错误。

比较 (8.575).toFixed(20)(8.575).toFixed(3) 并想象这个命题:8.575 < real("8.575"),其中 real 是一个虚函数,它创建一个具有无限精度的实数。

原来的数字不符合预期,已经引入了不准确。

我能想到的一个快速“解决方法”是:乘以 1000(或酌情),得到 toFixed(0)(仍有限制,但很荒谬),然后以十进制形式推回。

编码愉快。

【讨论】:

  • 当然,我不能在我的同一个函数的覆盖中使用“toFixed”。我不得不坚持使用“Math.round”,但你的回答为我指明了正确的方向。泰
  • @Joshua:是的,你可以。只需将 toFixed` 的副本存储在变量中,然后将 apply() 存储到数字中。
【解决方案2】:

感谢您的回答。我的实现几乎成功了,但在某些情况下因为浮点错误而没有成功。

我函数中的这一行是罪魁祸首: Math.round(这个 * 因子)

(它在 Number.prototype 上,所以“this”是数字); 8.575 * 100 得出 857.4999999999999,然后向下舍入。 这可以通过将行更改为如下来纠正: Math.round(Math.round(this * factor * 100) / 100)

我的整个解决方法现在更改为:

Number.prototype.toFixed = function(decimalPlaces) {
    var factor = Math.pow(10, decimalPlaces || 0);
    var v = (Math.round(Math.round(this * factor * 100) / 100) / factor).toString();
    if (v.indexOf('.') >= 0) {
        return v + factor.toString().substr(v.length - v.indexOf('.'));
    }
    return v + '.' + factor.toString().substr(1);
};

【讨论】:

    【解决方案3】:

    一致的解决方案是在四舍五入之前为每个数字添加一个固定容差 (epsilon)。它应该很小,但不能太小。

    例如,eps = 1e-9,这个:

    console.log((8.555).toFixed(2));    // returns 8.56
    console.log((8.565).toFixed(2));    // returns 8.57
    console.log((8.575).toFixed(2));    // returns 8.57
    console.log((8.585).toFixed(2));    // returns 8.59
    

    变成这样:

    console.log((8.555 + eps).toFixed(2));    // returns 8.56
    console.log((8.565 + eps).toFixed(2));    // returns 8.57
    console.log((8.575 + eps).toFixed(2));    // returns 8.58
    console.log((8.585 + eps).toFixed(2));    // returns 8.59
    

    【讨论】:

      【解决方案4】:

      也许它会对某人有所帮助,这是固定的流行 formatMoney() 函数,但具有正确的舍入。

      Number.prototype.formatMoney = function() {
        var n = this,
        decPlaces = 2,
        decSeparator = ",",
        thouSeparator = " ",
        sign = n < 0 ? "-" : "",
        i = parseInt(n = Math.abs(+n || 0)) + "",
        j = (j = i.length) > 3 ? j % 3 : 0,
        decimals = Number(Math.round(n +'e'+ decPlaces) +'e-'+ decPlaces).toFixed(decPlaces),
        result = sign + (j ? i.substr(0, j) + thouSeparator : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thouSeparator) + (decPlaces ? decSeparator + Math.abs(decimals-i).toFixed(decPlaces).slice(2) : "");
        return result;
      };
      
      (9.245).formatMoney(); // returns 9,25
      (7.5).formatMoney();   // returns 7,50
      (8.575).formatMoney(); // returns 8,58
      

      【讨论】:

        【解决方案5】:

        Check my answer

        function toFixed( num, precision ) {
            return (+(Math.round(+(num + 'e' + precision)) + 'e' + -precision)).toFixed(precision);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-06-16
          • 1970-01-01
          • 2017-09-02
          • 2010-11-29
          • 2012-02-17
          • 2015-01-14
          相关资源
          最近更新 更多