Содержание
Главное правило
Исходить из задач бизнеса. Обговорить какая точность нужна. Храним до 5 знаков после запятой, либо достаточно 2-3 знаков, либо копейки вообще не нужны.
Заранее согласовать правило округления и использовать только его.
После того как приняли стандарт хранения денег, и в приложении и в базе данных надо следовать этим правилам.
Best practice по хранению денег в базе данных(на примере mysql)
Если бы мне давали 10 центов каждый раз когда я вижу как кто-нибудь использует FLOAT для хранения денег, то у меня бы уже было $999.997634
Bill Karwin
- Не хранить деньги как float, double, real. Эти типы данных с плавающей запятой могут хранить огромные значения, но точность вычислений не высока.
- Подходящие типы numeric, decimial и integer(в некоторых случаях), biginteger. У каждого варианта есть свои плюсы и минусы.
- Если выбираем numeric , decimial (5,2) то в базе данных деньги хранятся в естественном виде(1200.50). В скобках указывается кол-во знаков до и после запятой. У этого способа хранения есть минус, если у вас предусмотрена работа с несколькими валютами, то придется ориентироваться по валюте, которая имеет наибольшее число знаков после запятой. Например в системе кроме рублей есть еще Бахрейский динар, который имеет 3 знака после запятой. Тогда и рубли будут храниться 1200.500 что для рублей будет избыточно.
- Если выбираем integer, biginteger то храним деньги в минимальной дробной денежной единице валюты. Т.е. если у нас рубли, то 1200.50 руб. будет сохранено в базе как 120050.
Best practice по работе с деньгами в php
- Если в БД деньги хранятся в decimial:
- То при получении сумы из БД в php число будет типа float и мы опять можем получить проблемы чисел с плавающей точкой. Есть даже сайт посвященный этому «What Every Programmer Should Know About Floating-Point Arithmetic»
- Чтобы избежать проблем чисел с плавающей точкой, надо использовать функции из расширения BCMath «Вычисления с произвольной точностью». Но тесты показывают, что работа с через BCMath медленнее.
- Если в БД деньги хранятся в biginteger:
- Постоянный перевод в копейки при сохранении в БД и перевод копеек в рубли при получении данных. Например на счет надо сохранить сумму 100.50 руб. Умнажаем её на 100 и сохраняем в бд. Когда вынимаем из бд, делаем деление на 100.
- Нет плавающей точки, погрешности исключены.
- Операции с целочисленными значениями быстрее.
- Минусы: постоянное умножение и деление.
Библиотеки
Можно воспользоваться готовым решением — это реализация класса «Деньги», довольно популярное решение, имеет более 2.5К звёзд.
Очень интересная статья