Проблемы обработки валют в JavaScript
Осуществлять действия с валютами в JavaScript несколько сложнее, чем кажется
Денежные значения обычно представлены числами с плавающей запятой - и это проблема, так как умножение и деление становятся довольно сложными операциями. Если сомневаетесь, попробуйте ввести в консоли 10/3
или 3.3*3
.
Конечно, иногда мы работаем с целыми числами, но гораздо чаще для представления фиатных валют нам требуются два знака после запятой, а при расчетах связанных с трейдингом или криптовалютами, после запятой могут потребоваться четыре, шесть, восемь и больше знаков.
Итак, нам нужно решить следующую проблему: как корректно делить и умножать действительные числа в JavaScript. Одно из решений - умножать действительное число на 10, пока оно не станет целым. Вместо того, чтобы умножать 3.3 на 3, можно умножить 33 на 3, а результат разделить на 10. Вот пример такого решения:
const multiplyMoney = (amount, multiplyBy, significantDecimals = 2) => {
const precision = Math.pow(10, significantDecimals);
const wholeAmount = amount * precision;
const result = Math.floor(wholeAmount * multiplyBy);
return result / precision;
};
Решение для деления носит сходный характер:
const divideMoney = (amount, divideBy, significantDecimals = 2) => {
const precision = Math.pow(10, significantDecimals);
const result = multiplyMoney(amount, 1 / divideBy, significantDecimals);
const remainder = ((amount * precision) % (result * precision)) / precision;
return [result, remainder];
};
console.log(3.111 * 2.1); // 6.533100000000001
console.log(multiplyMoney(3.111, 2.1, 6)); // 6.5331
console.log(multiplyMoney(3.111, 2.1)); // 6.53
console.log(10 / 3); // 3.3333333333333335
console.log(divideMoney(10, 3)); // [ 3.33, 0.01 ]
console.log(divideMoney(10, 3, 0)); // [ 3, 1 ]
console.log(divideMoney(10, 3, 1)); // [ 3.3, 0.1 ]
К счастью, давно прошли те дни, когда для корреткного отображения денежного значения нужно было добавлять строку с символом национальной валюты. Если Вы до сих пор так делаете, просто изучите API Intl.NumberFormat
Intl.NumberFormat - стандартный встроенный конструктор объектов JavaScript (дополнение к ядру ECMAScript), включающий языкозависимое форматирование.
Локаль определяет, как числа будут представлены для определенного языка. Некоторые языки используют запятую, чтобы отделить тысячи, и точку, чтобы отделить десятичные значение. У других языков все может быть наоборот:
- США: 1,000,000.5
- Индия: 1,00,00,000.5
- Россия: 1 000,5
Валюта определяет символ, локаль - формат числового значения:
let number = 1000000.5;
let options = { style: 'currency', currency: 'USD' };
console.log(new Intl.NumberFormat('en-US', options).format(number));
// $1,000,000.50
console.log(new Intl.NumberFormat('in-IN', options).format(number));
// US$1.000.000,50
console.log(new Intl.NumberFormat('ru-RU', options).format(number));
// 1 000 000,50 $US
options = { style: 'currency', currency: 'EUR' };
console.log(new Intl.NumberFormat('en-US', options).format(number));
// €1,000,000.50
console.log(new Intl.NumberFormat('in-IN', options).format(number));
// €1.000.000,50
console.log(new Intl.NumberFormat('ru-RU', options).format(number));
// 1 000 000,50 €
Еще одно преимущество Intl.NumberFormat - это то, что данный конструктор входит в состав нативного API, а значит поддержка данного функционала браузерами будет осуществляться и развиваться в дальнейшем. А это одна из весомых причин, для того, чтобы использовать данный функционал в своем коде.
Спасибо за внимание.