破解到固定的实现

时间:2011-03-30 18:42:27

标签: javascript

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

6 个答案:

答案 0 :(得分:7)

谢谢你的答案。我的实现几乎起作用,但在某些情况下由于浮点错误而没有。

我职能中的这一行是罪魁祸首: 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);
};

答案 1 :(得分:5)

这是因为浮点错误。

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

原始数字不符合预期,并且已经引入了不准确性。

我能想到的一个快速的“工作原理”是:乘以1000(或适当的话),获得toFixed(0)(仍然有一个限制,但这是荒谬的),然后以十进制形式推回

快乐的编码。

答案 2 :(得分:2)

这可能与浮点问题有关,请参阅How to deal with floating point number precision in JavaScript?

答案 3 :(得分:1)

也许它会帮助某人,这是固定的流行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

答案 4 :(得分:1)

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

例如,对于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

答案 5 :(得分:0)

Check my answer

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