Подтвердить что ты не робот

Прерванная реализация toFixed

По умолчанию реализация 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);
};
4b9b3361

Ответ 1

Это связано с ошибками с плавающей запятой.

Сравните (8.575).toFixed(20) с (8.575).toFixed(3) и представьте это предложение: 8.575 < real("8.575"), где real - это мнимая функция, которая создает вещественное число с бесконечной точностью.

То есть исходное число не соответствует ожидаемому, и неточность уже введена.

Одна быстрая "рабочая нагрузка", о которой я могу думать, заключается в следующем: умножить на 1000 (или, если необходимо), получить toFixed(0) от этого (все еще есть предел, но это абсурдно), а затем вернуться в десятичной форме.

Счастливое кодирование.

Ответ 2

Спасибо за ответ pst. Моя реализация почти сработала, но не в некоторых случаях из-за ошибок с плавающей запятой.

эта строка в моей функции является виновником: Math.round(этот * коэффициент)

(это на Number.prototype, поэтому "this" - это число); 8.575 * 100 выходит на 857.4999999999999, что в свою очередь округляет. это исправляется путем изменения строки следующим образом: Math.round(Math.round(этот * коэффициент * 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);
};

Ответ 5

Возможно, это поможет кому-то, это фиксированная популярная функция 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