Как бороться с точностью чисел с плавающей точкой в ​​JavaScript?

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

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

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

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

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

520
задан Derek Pollard 18 July 2019 в 21:35
поделиться

6 ответов

Как насчет toLocaleString метод?

> 0.1+0.2
< 0.30000000000000004

> (0.1+0.2).toLocaleString()
< 0.3

, Если Вы хотите, чтобы он продолжил свое вычислимое.

Number.parseFloat((0.1+0.2).toLocaleString())

Number.prototype.toLocaleString ()

, Почему бы не посмотреть документ ?

> 1000.021+0.02
< 1000.0409999999999


> (1000.021+0.02).toLocaleString()
< "1,000.041"

/// why not use this?
> (1000.021+0.02).toLocaleString("zh",{useGrouping: false,maximumFractionDigits: 10})
< "1000.041"
-1
ответ дан zheng 17 September 2019 в 06:19
поделиться
  • 1
    They' ре, не ОЧЕНЬ полезное, но иногда they' ре, полезное, где иначе метод был бы наиболее логичным решением (воображают метод, содержащий, например, цикл и Вы хочет возвратиться из метода в цикле). Существуют один или два примера в источнике JDK, если Вы смотрите тщательно. – Neil Coffey 15 April 2010 в 00:24

Изящный, Предсказуемый, и Допускающий повторное использование

Позволяют нам иметь дело с проблемой изящным способом допускающий повторное использование путь. Следующие семь строк позволят Вам получить доступ к точности с плавающей точкой, которой Вы требуете на любом числе просто путем добавления .decimal до конца числа, формулы, или созданный в Math функция.

// First extend the native Number object to handle precision. This populates
// the functionality to all math operations.

Object.defineProperty(Number.prototype, "decimal", {
  get: function decimal() {
    Number.precision = "precision" in Number ? Number.precision : 3;
    var f = Math.pow(10, Number.precision);
    return Math.round( this * f ) / f;
  }
});


// Now lets see how it works by adjusting our global precision level and 
// checking our results.

console.log("'1/3 + 1/3 + 1/3 = 1' Right?");
console.log((0.3333 + 0.3333 + 0.3333).decimal == 1); // true

console.log(0.3333.decimal); // 0.333 - A raw 4 digit decimal, trimmed to 3...

Number.precision = 3;
console.log("Precision: 3");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0.01
console.log((0.0008 + 0.0002).decimal); // 0.001

Number.precision = 2;
console.log("Precision: 2");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0.01
console.log((0.0008 + 0.0002).decimal); // 0

Number.precision = 1;
console.log("Precision: 1");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0
console.log((0.0008 + 0.0002).decimal); // 0

Number.precision = 0;
console.log("Precision: 0");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0
console.log((0.008 + 0.002).decimal); // 0
console.log((0.0008 + 0.0002).decimal); // 0

За Ваше здоровье!

-1
ответ дан Bernesto 17 September 2019 в 06:19
поделиться

decimal.js, big.js или bignumber.js может использоваться для предотвращения проблем управления с плавающей точкой в JavaScript:

0.1 * 0.2                                // 0.020000000000000004
x = new Decimal(0.1)
y = x.times(0.2)                          // '0.2'
x.times(0.2).equals(0.2)                  // true

big.js: минималист; простой в использовании; точность указана в десятичных разрядах; точность относилась к подразделению только.

bignumber.js: основания 2-64; параметры конфигурации; NaN; Бесконечность; точность указана в десятичных разрядах; точность относилась к подразделению только; основные префиксы.

decimal.js: основания 2-64; параметры конфигурации; NaN; Бесконечность; полномочия нецелого числа, exp, ln, журнал; точность указана в значащих цифрах; точность всегда применяется; случайные числа.

ссылка на подробные сравнения

1
ответ дан 22 November 2019 в 22:18
поделиться

Постарайтесь не иметь дело с плавающими точками во время операции с помощью Целых чисел

, Как указано на наиболее проголосовавшем ответе до сих пор, Вы можете работа с целыми числами , который означал бы умножать все Ваши факторы на 10 для каждого десятичного числа, Вы работаете с и делите результат на то же используемое число.

, Например, если Вы работаете с 2 десятичными числами, Вы умножаете все свои факторы на 100 прежде, чем сделать операцию и затем делите результат на 100.

Вот пример, Result1 является обычным результатом, Result2 использует решение:

var Factor1="1110.7";
var Factor2="2220.2";
var Result1=Number(Factor1)+Number(Factor2);
var Result2=((Number(Factor1)*100)+(Number(Factor2)*100))/100;
var Result3=(Number(parseFloat(Number(Factor1))+parseFloat(Number(Factor2))).toPrecision(2));
document.write("Result1: "+Result1+"<br>Result2: "+Result2+"<br>Result3: "+Result3);

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

0
ответ дан 22 November 2019 в 22:18
поделиться

Решенный это первым созданием обоих целых чисел чисел, выполнением выражения и впоследствии делением результата для возвращения десятичных разрядов:

function evalMathematicalExpression(a, b, op) {
    const smallest = String(a < b ? a : b);
    const factor = smallest.length - smallest.indexOf('.');

    for (let i = 0; i < factor; i++) {
        b *= 10;
        a *= 10;
    }

    a = Math.round(a);
    b = Math.round(b);
    const m = 10 ** factor;
    switch (op) {
        case '+':
            return (a + b) / m;
        case '-':
            return (a - b) / m;
        case '*':
            return (a * b) / (m ** 2);
        case '/':
            return a / b;
    }

    throw `Unknown operator ${op}`;
}

Результаты для нескольких операций (исключенные числа являются результатами eval):

0.1 + 0.002   = 0.102 (0.10200000000000001)
53 + 1000     = 1053 (1053)
0.1 - 0.3     = -0.2 (-0.19999999999999998)
53 - -1000    = 1053 (1053)
0.3 * 0.0003  = 0.00009 (0.00008999999999999999)
100 * 25      = 2500 (2500)
0.9 / 0.03    = 30 (30.000000000000004)
100 / 50      = 2 (2)
0
ответ дан 22 November 2019 в 22:18
поделиться

Я выглядел одинаково, фиксируют, и я решил что, если Вы включаете целое число как 1 и оцениваете это console.log(0.1 * 0.2 + 1);. Который приводит к 1.02. Это может привыкнуть к раунду исходная x переменная к корректной сумме.

, После того как длина десятичных разрядов 2 получена в Вашем примере, мы можем затем использовать его с эти toFixed() функция к раунду исходная x переменная правильно.

Видят в коде относительно того, что эта функция делает в прокомментированных разделах.

var myX= 0.2 * 0.1;
var myX= 42.5-42.65;
var myX= 123+333+3.33+33333.3+333+333;

console.log(myX);
// Outputs (example 1): 0.020000000000000004
// Outputs (example 2): -0.14999999999999858
// Outputs (example 3): 34458.630000000005
// Wrong

function fixRoundingError(x) {
// 1. Rounds to the nearest 10
//    Also adds 1 to round of the value in some other cases, original x variable will be used later on to get the corrected result.
var xRound = eval(x.toFixed(10)) + 1;
// 2. Using regular expression, remove all digits up until the decimal place of the corrected equation is evaluated..
var xDec = xRound.toString().replace(/\d+\.+/gm,'');
// 3. Gets the length of the decimal places.
var xDecLen = xDec.length;
// 4. Uses the original x variable along with the decimal length to fix the rounding issue.
var x = eval(x).toFixed(xDecLen);
// 5. Evaluate the new x variable to remove any unwanted trailing 0's should there be any.
return eval(x);
}

console.log(fixRoundingError(myX));
// Outputs (example 1): 0.02
// Outputs (example 2): -0.15
// Outputs (example 3): 34458.63
// Correct

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

0
ответ дан 22 November 2019 в 22:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: