Как бороться с точностью чисел с плавающей точкой в ​​JavaScript? - PullRequest
511 голосов
/ 22 сентября 2009

У меня есть следующий фиктивный скрипт:

function test(){
    var x = 0.1 * 0.2;
    document.write(x);
}
test();

Это напечатает результат 0.020000000000000004, тогда как он должен просто напечатать 0.02 (если вы используете свой калькулятор). Насколько я понял, это связано с ошибками в точности умножения с плавающей запятой.

У кого-нибудь есть хорошее решение, чтобы в таком случае я получил правильный результат 0.02? Я знаю, что есть такие функции, как toFixed, иначе возможна округление, но я бы хотел напечатать целое число без обрезки и округления Просто хотел узнать, есть ли у кого-нибудь хорошее, элегантное решение.

Конечно, иначе я округлю до 10 или около того цифр.

Ответы [ 34 ]

3 голосов
/ 16 сентября 2017

Обратите внимание, что для общего назначения такое поведение, вероятно, будет приемлемым.
Проблема возникает при сравнении этих значений с плавающей запятой для определения соответствующего действия.
С появлением ES6 новая константа Number.EPSILON определена для определения допустимой погрешности:
Таким образом, вместо выполнения сравнения, как это

0.1 + 0.2 === 0.3 // which returns false

вы можете определить пользовательскую функцию сравнения, например:

function epsEqu(x, y) {
    return Math.abs(x - y) < Number.EPSILON;
}
console.log(epsEqu(0.1+0.2, 0.3)); // true

Источник: http://2ality.com/2015/04/numbers-math-es6.html#numberepsilon

2 голосов
/ 14 декабря 2014

У меня была проблема с ошибками округления в моде 3. Иногда, когда я получал 0, я получал .000 ... 01. Это достаточно просто для обработки, просто проверьте на <= .01. Но тогда иногда я получал бы 2.99999999999998. ОЙ! </p>

BigNumbers решил проблему, но представил другую, несколько ироническую проблему. Когда я пытался загрузить 8.5 в BigNumbers, мне сообщили, что это действительно 8.4999… и в нем более 15 значащих цифр. Это означало, что BigNumbers не могли принять это (я полагаю, я упоминал, что эта проблема была несколько ироничной).

Простое решение иронической проблемы:

x = Math.round(x*100);
// I only need 2 decimal places, if i needed 3 I would use 1,000, etc.
x = x / 100;
xB = new BigNumber(x);
1 голос
/ 22 сентября 2009

не элегантно, но делает работу (удаляет завершающие нули)

var num = 0.1*0.2;
alert(parseFloat(num.toFixed(10))); // shows 0.02
1 голос
/ 08 октября 2014

Использовать номер (1.234443). Исправлено (2); будет напечатано 1.23

function test(){
    var x = 0.1 * 0.2;
    document.write(Number(x).toFixed(2));
}
test();
1 голос
/ 22 сентября 2009

Использование

var x = 0.1*0.2;
 x =Math.round(x*Math.pow(10,2))/Math.pow(10,2);
1 голос
/ 07 августа 2010

Вы правы, причина этого - ограниченная точность чисел с плавающей запятой. Храните свои рациональные числа как деление двух целых чисел, и в большинстве случаев вы сможете хранить числа без потери точности. Когда дело доходит до печати, вы можете отобразить результат в виде дроби. С представлением, которое я предложил, оно становится тривиальным.

Конечно, это не очень поможет с иррациональными числами. Но вы можете оптимизировать свои вычисления таким образом, чтобы они вызывали наименьшую проблему (например, обнаружение ситуаций, таких как sqrt(3)^2).

).
0 голосов
/ 10 июля 2017

Я не очень хорош в программировании, но мне действительно была интересна эта тема, поэтому я попытался понять, как решить эту проблему без использования каких-либо библиотек или скриптов

Я написал это на блокноте

var toAlgebraic = function(f1, f2) {
    let f1_base = Math.pow(10, f1.split('.')[1].length);
    let f2_base = Math.pow(10, f2.split('.')[1].length);
    f1 = parseInt(f1.replace('.', ''));
    f2 = parseInt(f2.replace('.', ''));

    let dif, base;
    if (f1_base > f2_base) {
        dif = f1_base / f2_base;
        base = f1_base;
        f2 = f2 * dif;
    } else {
        dif = f2_base / f1_base;
        base = f2_base;
        f1 = f1 * dif;
    }

    return (f1 * f2) / base;
};

console.log(0.1 * 0.2);
console.log(toAlgebraic("0.1", "0.2"));

вам может понадобиться рефакторинг этого кода, потому что я не очень хорош в программировании:)

0 голосов
/ 14 июля 2016

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

console.log ((parseFloat (0.1) + parseFloat (0.2)). toFixed (1) == parseFloat (0.3) .toFixed (1));
0 голосов
/ 20 июня 2014

Вывод с использованием следующей функции:

var toFixedCurrency = function(num){
    var num = (num).toString();
    var one = new RegExp(/\.\d{1}$/).test(num);
    var two = new RegExp(/\.\d{2,}/).test(num);
    var result = null;

    if(one){ result = num.replace(/\.(\d{1})$/, '.$10');
    } else if(two){ result = num.replace(/\.(\d{2})\d*/, '.$1');
    } else { result = num*100; }

    return result;
}

function test(){
    var x = 0.1 * 0.2;
    document.write(toFixedCurrency(x));
}

test();

Обратите внимание на вывод toFixedCurrency(x).

0 голосов
/ 03 октября 2013

Это работает для меня:

function round_up( value, precision ) { 
    var pow = Math.pow ( 10, precision ); 
    return ( Math.ceil ( pow * value ) + Math.ceil ( pow * value - Math.ceil ( pow * value ) ) ) / pow; 
}

round_up(341.536, 2); // 341.54
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...