Лучшая практика для работы с валютными ценностями в PHP?
Мне нужно добавить, умножить и сравнить валютные значения в PHP, и нужно быть уверенным, что это точно до одного цента.
Один из способов - хранить все в поплавке, использовать круглые до и после каждой операции и машины умения epsilon при сравнении для равенства. Довольно громоздкое имхо.
Другим способом является сохранение всего объекта в виде центов в целых типах данных, плюс не забудьте конвертировать назад и вперед в любое время, когда я работаю с базой данных (mysql, где я использую десятичный тип данных). Inelegant, и много ошибок ошибок, imho.
Другой способ - создать собственный "тип данных", сохранить все значения в строках ( "34.12" ) и создать свои собственные функции математической замены. Эта функция будет преобразовывать значение в целые числа внутри, выполнять вычисления и выводить результат снова на строки. Удивительно сложный, imho.
Мой вопрос: какова наилучшая практика для работы с валютными ценностями в PHP?
Спасибо!
Ответы
Ответ 1
Позвольте мне ответить на это сам. Лучшей практикой было бы создать класс "денег". Этот класс хранит внутреннее количество как центов в целых числах и предлагает геттеры, сеттеры, математические функции, такие как добавление, вычитание и сравнение. Гораздо чище.
Ответ 2
Начиная с MySQL v5.0.3, тип данных MySQL DECIMAL
хранит точное десятичное число, т.е. неточное представление с плавающей запятой.
Для правильного управления точками точности в PHP используйте произвольные математические функции точности. Внутренне эта библиотека манипулирует текстовыми строками.
Валюта предназначена для хранения в виде десятичного числа, вы можете получить единицы денег, меньшие, чем центы. Вы должны округлять любые значения при отображении фигур, а не во время манипуляций или хранения.
Ответ 3
Обновление 2016:
Спустя пару лет и, надеюсь, немного мудрее;)
Поскольку этот ответ по-прежнему получает периодическое голосование, я почувствовал необходимость пересмотра своего ответа. Я абсолютно не советую хранить "деньги" как целые числа. Единственный истинный ответ - это подход Эндрю Данна: используйте тип Mysql DECIMAL и инкапсулируйте функции php bc_ * в класс Currency.
Я сохраню свой устаревший ответ здесь ради полноты
Вы всегда должны работать с целыми числами. Сохраните значения как ints в вашей базы данных, используйте ints для выполнения вычислений и когда и только тогда, вы хотите распечатать его, преобразовать в некоторый читаемый формат.
Таким образом, вы полностью преодолеваете проблемы с плавающей запятой, что, как правило, является большой проблемой при работе с деньгами;)
Изменить: стоит упомянуть: вам нужно подумать о точности прежде чем вы начнете работать таким образом. Как указывали другие, вы возможно, придется работать со значениями, меньшими, чем цент, поэтому вам нужно сделать ваше умножение/разделение соответственно. (т.е. валюта * 1000 за .001)
Ответ 4
Сохраняйте значения как центы и используйте целые числа. Напишите классы или вспомогательные функции для инкапсуляции отображения в пользовательском интерфейсе и обработки значений в запросах базы данных. Если вы используете float, существует слишком много риска, что вы потеряете где-нибудь цент.
Ответ 5
Я попытался умножить числа с плавающей запятой на 100 перед всеми математическими операциями, а затем разделить на 100 перед отображением результата. (Это сработало!)
Математически, это предлагает "преобразование в целые числа", как и все остальные, но код выглядит намного более элегантным без каких-либо функций преобразования. Кроме того, чтобы избежать путаницы, я просто добавил "центы" в конце имен всех значений.
Со стороны машины, вероятно, быстрее использовать целые числа вместо числа с плавающей запятой, но при написании понятного человеку кода я предпочитаю свое решение. (Ваш пробег может отличаться, но он работал в моем приложении.)