Вероятно, вам следует масштабировать десятичные значения на 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) .
Даниэль Вассалло
источник
3000.57 + 0.11 === 3000.68
возвращаетсяfalse
.Решение - масштабирование каждого значения на 100. Делать это вручную, вероятно, бесполезно, поскольку вы можете найти библиотеки, которые сделают это за вас. Я рекомендую moneysafe, который предлагает функциональный API, хорошо подходящий для приложений ES6:
https://github.com/ericelliott/moneysafe
Работает как в Node.js, так и в браузере.
источник
in$, $
имена значений неоднозначны для тех, кто раньше не использовал пакет. Я знаю, что это был выбор Эрика называть вещи таким образом, но я все же считаю, что это достаточно большая ошибка, что я, вероятно, переименую их в операторе import / destructured require.Money$afe has not yet been tested in production at scale.
. Просто указываю на это, чтобы каждый мог подумать, подходит ли это для егоНет такой вещи, как «точный» финансовый расчет, потому что всего две десятичные дроби, но это более общая проблема.
В JavaScript вы можете масштабировать каждое значение на 100 и использовать
Math.round()
каждый раз, когда может появиться дробь.Вы можете использовать объект для хранения чисел и включить округление в его
valueOf()
метод прототипов . Как это:Таким образом, каждый раз, когда вы используете объект Money, он будет округлен до двух знаков после запятой. Неокругленное значение по-прежнему доступно через
m.amount
.Вы можете встроить свой собственный алгоритм округления
Money.prototype.valueOf()
, если хотите.источник
Money(0.1)
означает, что лексер JavaScript считывает строку «0.1» из источника и затем преобразует ее в двоичную числа с плавающей запятой, а затем вы уже выполнили непреднамеренное округление. Проблема заключается в представлении (двоичное против десятичного), а не в точности .используйте decimaljs ... Это очень хорошая библиотека, которая решает суровую часть проблемы ...
просто используйте его во всех своих операциях.
https://github.com/MikeMcl/decimal.js/
источник
Ваша проблема связана с неточностью вычислений с плавающей запятой. Если вы просто используете округление для решения этой проблемы, у вас будет большая ошибка при умножении и делении.
Решение ниже, объяснение следует:
Вам нужно подумать о математике, стоящей за этим, чтобы понять это. Реальные числа, такие как 1/3, не могут быть представлены в математике с десятичными значениями, поскольку они бесконечны (например, - .333333333333333 ...). Некоторые числа в десятичном формате не могут быть правильно представлены в двоичном формате. Например, 0,1 не может быть правильно представлено в двоичном формате с ограниченным количеством цифр.
Более подробное описание смотрите здесь: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Взгляните на реализацию решения: http://floating-point-gui.de/languages/javascript/
источник
Из-за двоичного характера их кодирования некоторые десятичные числа не могут быть представлены с идеальной точностью. Например
Если вам нужно использовать чистый javascript, вам нужно думать о решении для каждого расчета. Для приведенного выше кода мы можем преобразовать десятичные дроби в целые числа.
Как избежать проблем с десятичной математикой в JavaScript
Есть специальная библиотека для финансовых расчетов с отличной документацией. Finance.js
источник
К сожалению, все ответы до сих пор игнорируют тот факт, что не все валюты имеют 100 подъединиц (например, цент - это подединица доллара США (USD)). Такие валюты, как иракский динар (IQD), состоят из 1000 субединиц: иракский динар состоит из 1000 филсов. Японская йена (JPY) не имеет дополнительных единиц. Так что «умножить на 100 для выполнения целочисленной арифметики» - не всегда правильный ответ.
Дополнительно для денежных расчетов вам также необходимо отслеживать валюту. Вы не можете добавить доллар США (USD) к индийской рупии (INR) (без предварительного преобразования одного в другой).
Также существуют ограничения на максимальное количество, которое может быть представлено целочисленным типом данных JavaScript.
В денежных расчетах вы также должны иметь в виду, что деньги имеют конечную точность (обычно 0–3 десятичных знака) и округление должно выполняться определенными способами (например, «обычное» округление по сравнению с округлением банкира). Тип округления также может зависеть от юрисдикции / валюты.
Как обращаться с деньгами в javascript есть очень хорошее обсуждение соответствующих моментов.
При поиске я нашел библиотеку dinero.js, которая решает многие проблемы с денежными расчетами . Я еще не использовал его в производственной системе, поэтому не могу дать осознанное мнение о нем.
источник