В интересах создания межплатформенного кода я хотел бы разработать простое финансовое приложение в 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... Это очень хорошая библиотека, которая решает жесткую часть проблемы...
просто используйте его во всех ваших операциях.
Ответ 6
Если вы хотите создать действительно точное приложение, вам не следует доверять библиотекам JavaScript. вместо этого вы можете использовать Web-Assembly.