Я унаследовал огромную кучу унаследованного кода, написанного на PHP, поверх базы данных MySQL. Я заметил, что приложение используетdoubles
для хранения и манипулирования данными.
Теперь я наткнулся на многочисленные посты с упоминанием того, как double
они не подходят для денежных операций из-за ошибок округления. Тем не менее, я еще не нашел полного решения о том, как денежные значения должны обрабатываться в коде PHP и храниться в базе данных MySQL.
Есть ли лучшая практика, когда дело доходит до обращения с деньгами именно в PHP?
Вещи, которые я ищу:
- Как данные должны храниться в базе данных? тип столбца? размер?
- Как данные должны обрабатываться в обычном сложении, вычитании. умножение или деление?
- Когда я должен округлить значения? Сколько округления приемлемо, если таковые имеются?
- Есть ли разница между обработкой больших денежных сумм и низкими?
Примечание: очень упрощенный пример кода , как я мог бы столкнуться ценности денег в повседневной жизни (различные вопросы безопасности были проигнорированы для упрощения Конечно , в реальной жизни я никогда бы не использовать мой код , как это.):
$a= $_POST['price_in_dollars']; //-->(ex: 25.06) will be read as a string should it be cast to double?
$b= $_POST['discount_rate'];//-->(ex: 0.35) value will always be less than 1
$valueToBeStored= $a * $b; //--> any hint here is welcomed
$valueFromDatabase= $row['price']; //--> price column in database could be double, decimal,...etc.
$priceToPrint=$valueFromDatabase * 0.25; //again cast needed or not?
Я надеюсь, что вы используете этот пример кода в качестве средства, чтобы выявить больше вариантов использования, а не воспринимать его буквально, конечно.
Бонусный вопрос Если я буду использовать ORM, такой как Doctrine или PROPEL, насколько отличается использование денег в моем коде.
decimal
тип в C # имеет ограниченную точность, но идеально подходит для денежных значений.Ответы:
Может быть довольно сложно обрабатывать числа с помощью PHP / MySQL. Если вы используете десятичное число (10,2), а ваше число длиннее или имеет более высокую точность, оно будет усечено без ошибок (если вы не установили правильный режим для своего сервера БД).
Для обработки больших значений или значений высокой точности вы можете использовать библиотеку типа BCMath это позволит вам выполнять основные операции с большими числами и сохранять требуемую точность.
Я не уверен, какие именно вычисления вы будете делать, но вы также должны иметь в виду, что (0,22 * 0,4576) + (0,78 * 0,4576) не будет равно 0,4576, если вы не будете использовать надлежащую точность в процессе.
Максимальный размер DECIMAL в MySQL составляет 65, поэтому его должно быть более чем достаточно для любых целей. Если вы используете тип поля DECIMAL, он будет возвращен в виде строки независимо от использования ORM или просто PDO / mysql (i).
DECIMAL с точностью, которая вам нужна. Если вы используете обменный курс, вам понадобится как минимум четыре знака после запятой
Используйте BCMath, чтобы быть на стороне сохранения и почему использование float не может быть хорошей идеей
Для денежных значений обычно допустимы два десятичных знака, но вам может потребоваться больше, если, например, вы используете обменные курсы.
Зависит от того, что вы подразумеваете под большим. Существует определенная разница между обработкой чисел с высокой точностью.
источник
Простой обходной путь - хранить их как целые числа. 99.99 хранится как 9999. Если это не сработает (и есть много причин, по которым это может быть неправильным выбором), вы можете использовать тип Decimal. http://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html на стороне mysql. На стороне PHP я нашел этот /programming/3244094/decimal-type-in-php который может быть тем, что вы ищете.
Бонусный вопрос: сложно сказать. Орм будет работать на основе выбранных типов данных. Я бы сказал, что вы можете сделать некоторые вещи с помощью абстракции, чтобы помочь, но эта конкретная проблема не решается просто путем перехода на ORM.
источник
Я постараюсь изложить свой опыт в этом:
Я использую
DECIMAL(10,2)
для mysql без проблем (8 завершений и 2 десятичных знака == 99.999.999,99 == огромное количество), но это зависит от диапазона денег, который вы должны покрыть. Огромные суммы следует принимать с особой осторожностью (например, максимальные значения ОС). В десятичной части я использую 2 значения, чтобы избежать усечения или округления значений. Что касается денег, то мало случаев, когда вам нужно больше десятичных знаков (в этом случае вам нужно убедиться, что пользователь будет работать со всеми из них, в противном случае это бесполезные данные)Работа с одной валютой и обменным столом (с датами). Таким образом вы гарантируете, что у вас всегда будет правильная сохраненная сумма. Дополнительно: сохранить полные значения и создать представление с результатами расчета. Это поможет вам зафиксировать значения на лету
Опять же, зависит от вашей системы диапазон денег. Всегда думайте в терминах KISS, если вам не нужно попадать в беспорядок обмена валюты
В зависимости от вашей ОС и языков программирования вам всегда нужно проверять максимальные и минимальные значения
источник
$valueToBeStored= $a * $b;
if$a
и$b
оба будут считаны из базы данных как десятичные числа, я думаю, что они будут преобразованыdouble
в PHP, верно? это повлияет на цифры?$a
и$b
взяты из дб? поэтому в моем примере вам не нужно хранить данные,$valueToBeStored
потому что вы всегда будете иметь источник$a
и$b
данные. Таким образом, вы можете программно работать со значением в функции или около того или создать представление MySQL с результатом столбца. Таким образом, если нужно изменить какое-либо значение, вам не нужно беспокоиться об изменении нескольких мест (подвержено ошибкам)