Точный финансовый расчет в JavaScript. Что такое готча?

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

Ответ 1

Вероятно, вы должны масштабировать свои десятичные значения на 100 и представлять все денежные значения целыми центами. Это делается для того, чтобы избежать проблем с логикой и арифметикой с плавающей запятой. В JavaScript нет десятичного типа данных - единственным числовым типом данных является плавающая запятая. Поэтому обычно рекомендуется обрабатывать деньги как 2550 центов вместо 25.50 долларов.

Учтите, что в JavaScript:

var result = 1.0 + 2.0;     // (result === 3.0) returns true

Но:

var result = 0.1 + 0.2;     // (result === 0.3) returns false

Выражение 0.1 + 0.2 === 0.3 возвращает false, но, к счастью, целочисленная арифметика в плавающей точке является точной, поэтому ошибки десятичного представления можно избежать, масштабируя 1.

Заметим, что, хотя множество действительных чисел бесконечно, их точное число (18,437,736,874,454,810,627) может быть представлено точно с помощью формата с плавающей запятой JavaScript. Поэтому представление других чисел будет приближением действительного числа 2.


1 Дуглас Крокфорд: JavaScript: Хорошие детали: Приложение A - Ужасные части (стр. 105) ).
2 Дэвид Фланаган: JavaScript: окончательное руководство, четвертое издание: 3.1.3 Литералы с плавающей запятой ( страница 31).

Ответ 2

Решение масштабирования каждого значения на 100. Делать это вручную, вероятно, бесполезно, поскольку вы можете найти библиотеки, которые делают это за вас. Я рекомендую moneysafe, который предлагает функциональный API, хорошо подходящий для приложений ES6:

const { in$, $ } = require('moneysafe');
console.log(in$($(10.5) + $(.3)); // 10.8

https://github.com/ericelliott/moneysafe

Работает как в Node.js, так и в браузере.

Ответ 3

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

В JavaScript вы можете масштабировать каждое значение на 100 и использовать Math.round() каждый раз, когда может произойти дробь.

Вы можете использовать объект для хранения чисел и включать округление в свой прототип valueOf(). Вот так:

sys = require('sys');

var Money = function(amount) {
        this.amount = amount;
    }
Money.prototype.valueOf = function() {
    return Math.round(this.amount*100)/100;
}

var m = new Money(50.42355446);
var n = new Money(30.342141);

sys.puts(m.amount + n.amount); //80.76569546
sys.puts(m+n); //80.76

Таким образом, каждый раз, когда вы используете Money-объект, он будет представлен как округленный до двух десятичных знаков. Неограниченное значение по-прежнему доступно через m.amount.

Вы можете построить свой собственный алгоритм округления в Money.prototype.valueOf(), если хотите.

Ответ 4

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

Решение ниже, объясняется следующее:

Вам нужно подумать о математике, чтобы понять это. Реальные числа, такие как 1/3, не могут быть представлены в математике с десятичными значениями, поскольку они бесконечны (например,.333333333333333...). Некоторые числа в десятичной форме не могут быть представлены в двоичном формате правильно. Например, 0,1 не может быть правильно изображен в двоичном формате с ограниченным числом цифр.

Более подробное описание смотрите здесь: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Взгляните на реализацию решения: http://floating-point-gui.de/languages/javascript/

Ответ 5

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

просто используйте его во всех ваших операциях.

https://github.com/MikeMcl/decimal.js/

Ответ 6

Если вы хотите создать действительно точное приложение, вам не следует доверять библиотекам JavaScript. вместо этого вы можете использовать Web-Assembly.