"Front-end BUG" - неточность округления toFixed

внешний интерфейс Vue.js React.js

вопрос

Однажды служба поддержки клиентов сообщила мне, что поступила жалоба клиента, в которой говорилось, что данные о сумме выставления счета были неверными, а тестовая обратная связь была правильной в браузере IE11.

После расследования было установлено, чтоtoFixed()вызванный.

причина

посмотриtoFixed()Разная производительность под хромом, Firefox и IE.

хром:

Fire Fox:

IE:

можно увидетьtoFixed()Округление не точное на хроме, фаерфоксе.

а такжеtoFixed()В Chrome и Firefox он не округляется банковским округлением, как упоминалось в Интернете.

Правило банковского метода округления: «округлить до пяти. Если число после пяти не равно нулю, прибавьте единицу. Если число после пяти равно нулю, см. четно-нечетное. Если оно четное до пяти, оно должно округлить. Если до пяти нечетно, прибавьте единицу».

Например, банковское округление равно(2.55).toFixed(1) = 2.5,(3.55).toFixed(1) = 3.5выше не совпадает.

Так почему же это происходит?Начнем с определения toFixed.спецификация ecmascriptПредставление toFixed:

Выполните шаги, показанные на рисунке выше, чтобы продемонстрировать(2.55).toFixed(1) = 2.5процесс обработки.

x равно 2,55, что меньше, чем102110^{21}, f равно 1, так чтоn÷10fxn\div10^f-xТочное математическое значение максимально близко к нулю, принимая n равным 25 и n равным 26,

Вы можете видеть, что ближайший к нулю должен быть -0,04999... , поэтому n равно 25, затем m равно 25, k равно 2,kfk-fравно 1, поэтому a равно 2, затем b равно 5, поэтому(2.55).toFixed(1)Результат2.5.

Как можно заметить(2.55).toFixed(1)Результат2.5вместо2.6,Да25/102.55=0.049999925/10-2.55=-0.0499999... причина, в то время как25/102.5525/10-2.55почему не равно0.50.5, причина и0.1+0.20.1+0.2спектр0.30.3да, ты можешь меня видетьэтот столбец.

Но в браузере IE выполните25/102.5525/10-2.55а также26/102.5526/10-2.55Результат такой же, как при выполнении в chrome и firefox. Здесь можно только сделать вывод, что toFixed, определенный в браузере IE, не соответствует спецификации ecmascript. Конкретная причина пока не ясна. Если вы знаете, вы можете оставить сообщение в комментариях, спасибо.

решить

Предполагая, что число, которое нужно округлить, равно числу, чтобы сохранить n знаков после запятой, вы можете сначала использоватьnumber*10nnumber*10^n, затем используйтеMath.round()собрали и окончательно удалили10n10^n, который косвенно реализует округление. Кроме тогоtoFixed()Также есть функция автоматического заполнения нулями, которую необходимо реализовать, поэтому нижеследующее просто инкапсулирует метод toFixed для достижения округления.

function toFixed(number, m) {
    if (typeof number !== 'number') {
        throw new Error("number不是数字");
    }
    let result = Math.round(Math.pow(10, m) * number) / Math.pow(10, m);
    result = String(result);
    if (result.indexOf(".") == -1) {
        if(m != 0){
            result += ".";
            result += new Array(m + 1).join('0');
        }
    } else {
        let arr = result.split('.');
        if (arr[1].length < m) {
            arr[1] += new Array(m - arr[1].length + 1).join('0')
        }
        result = arr.join('.')
    }
    return result
}