Heredé una gran cantidad de código heredado escrito en PHP sobre una base de datos MySQL. Lo que noté es que la aplicación utiliza doubles
para el almacenamiento y la manipulación de datos.
Ahora me encontré con numerosas publicaciones que mencionan cómo double
no son adecuadas para las operaciones monetarias debido a los errores de redondeo. Sin embargo, todavía tengo que encontrar una solución completa sobre cómo se deben manejar los valores monetarios en el código PHP y almacenarlos en una base de datos MySQL.
¿Existe una mejor práctica cuando se trata de manejar dinero específicamente en PHP?
Las cosas que estoy buscando son:
- ¿Cómo deben almacenarse los datos en la base de datos? tipo de columna? ¿Talla?
- ¿Cómo deben manejarse los datos en suma, resta normales? multiplicación o división?
- ¿Cuándo debo redondear los valores? ¿Cuánto redondeo es aceptable si hay alguno?
- ¿Hay alguna diferencia entre manejar valores monetarios grandes y valores bajos?
Nota: Un código de muestra MUY simplificado de cómo podría encontrar valores monetarios en la vida cotidiana (varias preocupaciones de seguridad fueron ignoradas para simplificar. Por supuesto, en la vida real nunca usaría mi código así):
$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?
Espero que use este código de muestra como un medio para sacar más casos de uso y no tomarlo literalmente, por supuesto.
Pregunta adicional Si voy a usar un ORM como Doctrine o PROPEL, ¿qué tan diferente será usar dinero en mi código?
decimal
tipo en C # tiene una precisión limitada pero se adapta perfectamente a los valores monetarios.Respuestas:
Puede ser bastante complicado manejar números con PHP / MySQL. Si usa decimal (10,2) y su número es más largo o tiene mayor precisión, se truncará sin error (a menos que configure el modo adecuado para su servidor de base de datos).
Para manejar valores grandes o valores de alta precisión, puede usar una biblioteca como BCMath, que le permitirá realizar operaciones básicas en los números grandes y mantener la precisión requerida.
No estoy seguro de qué cálculos exactamente hará, pero también debe tener en cuenta que (0.22 * 0.4576) + (0.78 * 0.4576) no será igual a 0.4576 si no utiliza la precisión adecuada durante el proceso.
El tamaño máximo de DECIMAL en MySQL es 65, por lo que debería ser más que suficiente para cualquier propósito. Si usa el tipo de campo DECIMAL, se devolverá como una cadena, independientemente del uso de un ORM o simplemente PDO / mysql (i).
DECIMAL con la precisión que necesitas. Si está utilizando tipos de cambio, necesitará al menos cuatro decimales
Use BCMath para estar del lado de guardar y por qué usar flotador puede no ser una buena idea
Para valores monetarios normales, dos decimales son aceptables, pero es posible que necesite más si, por ejemplo, está utilizando tipos de cambio.
Depende de lo que quieras decir con grande. Definitivamente hay una diferencia entre manejar números con alta precisión.
fuente
Una solución simple es almacenarlos como enteros. 99.99 almacenado como 9999. Si esto no funciona (y hay muchas razones por las que esto podría ser una mala elección), puede usar el tipo Decimal. http://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html en el lado de mysql. En el lado de php encontré este /programming/3244094/decimal-type-in-php que podría ser lo que buscas.
Pregunta extra: difícil de decir. El orm funcionará en función de los tipos de datos elegidos. Diría que podría hacer algunas cosas con abstracción para ayudar, pero este problema específico no se aborda simplemente moviéndose a un ORM.
fuente
Trataré de poner mi experiencia en esto:
He estado usando
DECIMAL(10,2)
mysql sin problemas (8 completos y 2 decimales == 99.999.999,99 == gran cantidad), pero esto depende del rango de dinero que deberá cubrir. Se deben tomar grandes cantidades con mucho cuidado (por ejemplo, valores flotantes máximos del sistema operativo). En la parte decimal utilizo 2 valores para evitar truncar ni redondear valores. En cuanto al dinero, hay pocos casos en los que necesita más decimales (en cuyo caso debe asegurarse de que el usuario trabaje con todos ellos; de lo contrario, son datos inútiles)Trabaje con una moneda y una tabla de cambio (con fechas). De esta manera, se asegura de que siempre tendrá la cantidad correcta guardada. Un extra: guardar valores completos y crear una vista con resultados de cálculo. Esto lo ayudará a fijar valores sobre la marcha.
Nuevamente, depende del rango de dinero de su sistema. Siempre piense en términos de KISS, a menos que necesite caer en el desorden del cambio de divisas.
Dependiendo de su sistema operativo y lenguajes de programación, siempre necesita verificar sus valores máximos y mínimos
fuente
$valueToBeStored= $a * $b;
si$a
y$b
ambos se leen como decimales de la base de datos, creo que se enviarán adouble
PHP ¿verdad? ¿afectará eso a los números?$a
y$b
se toman de db? por lo tanto, en mi ejemplo, no necesita almacenar nunca$valueToBeStored
porque siempre tendrá la fuente$a
y los$b
datos. Por lo tanto, puede trabajar programáticamente el valor en una función o crear una vista mysql con el resultado de la columna. De esta manera, si se debe cambiar algún valor, no tiene que preocuparse por modificar varios lugares (propenso a errores)