Недавно мы внедрили систему, которая должна обрабатывать значения в нескольких валютах и конвертировать их между собой, и выяснила некоторые трудности.
НИКОГДА НЕ ИСПОЛЬЗУЙТЕ НОМЕРА С ПЛАВУЩИМИ ТОЧКАМИ ДЛЯ ДЕНЕГ
Арифметика с плавающей запятой вносит неточности, которые могут быть не замечены, пока они не напортачили. Все значения должны храниться в виде целых чисел или типов с фиксированной десятичной дробью, и если вы решите использовать тип с фиксированной десятичной дробью, убедитесь, что вы точно понимаете, что этот тип делает под капотом (т.е. использует ли он внутренне целое число или число с плавающей запятой) типа).
Когда вам нужно сделать расчеты или преобразования:
- Преобразование значений в число с плавающей запятой
- Рассчитать новое значение
- Округлить число и преобразовать его обратно в целое число
При преобразовании числа с плавающей запятой обратно в целое число на шаге 3 не просто приводите его - используйте математическую функцию, чтобы сначала округлить его. Обычно это будет round
, хотя в особых случаях это может быть floor
или ceil
. Знайте разницу и тщательно выбирайте.
Сохраните тип числа вместе со значением
Это может быть не так важно для вас, если вы работаете только с одной валютой, но для нас это было важно при работе с несколькими валютами. Мы использовали трехсимвольный код для валюты, такой как USD, GBP, JPY, EUR и т. Д.
В зависимости от ситуации, также может быть полезно хранить:
- Является ли число до или после налога (и какая ставка налога была)
- Является ли число результатом преобразования (и из чего оно было преобразовано)
Знайте границы точности чисел, с которыми вы имеете дело
Для реальных значений вы хотите быть точным, как наименьшая единица валюты. Это означает, что у вас нет значений меньше цента, пенни, иены, фена и т. Д. Не сохраняйте значения с большей точностью, чем без причины.
Внутренне, вы можете иметь дело с меньшими значениями, в этом случае это другой тип значения валюты . Убедитесь, что ваш код знает, что есть, и не перепутает их. Избегайте использования значений с плавающей запятой даже здесь.
Сложив все эти правила вместе, мы определились со следующими правилами. В рабочем коде валюты хранятся с использованием целого числа для наименьшей единицы.
class Currency {
String code; // eg "USD"
int value; // eg 2500
boolean converted;
}
class Price {
Currency grossValue;
Currency netValue;
Tax taxRate;
}
В базе данных значения хранятся в виде строки в следующем формате:
USD:2500
Это хранит значение $ 25,00. Мы смогли сделать это только потому, что код, работающий с валютами, не обязательно должен находиться внутри самого уровня базы данных, поэтому все значения можно сначала преобразовать в память. Другие ситуации, без сомнения, поддаются другим решениям.
И если раньше я не уточнил, не используйте float!