如何在PHP和MySql中处理货币值?


16

我继承了在MySQL数据库之上用PHP编写的大量旧代码。我注意到的是该应用程序doubles用于存储和处理数据。

现在,我遇到了许多帖子,提到double由于四舍五入的错误,它们不适合货币操作。但是,对于如何在PHP代码中处理货币值并将其存储在MySQL数据库中,我尚未找到完整的解决方案。

在专门用PHP处理资金时,是否有最佳实践?

我正在寻找的东西是:

  1. 数据应如何存储在数据库中?列类型?尺寸?
  2. 正常的加,减数据应如何处理。乘法还是除法?
  3. 我应该什么时候取整值?舍入多少舍入(如果有)?
  4. 处理大货币值和低货币值之间有区别吗?

注意: 一个非常简化的示例代码,说明了我在日常生活中可能会遇到的货币价值(为简化起见,忽略了各种安全问题。当然,在现实生活中,我永远不会像这样使用我的代码):

$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?

我希望您使用此示例代码作为提出更多用例的手段,而不是从字面上看。

额外的问题 如果我要使用诸如Doctrine或PROPEL之类的ORM,那么在我的代码中使用金钱有何不同。


1
我不知道PHP,但是知道术语在这些情况下对于fu是非常有用的,您要查找的术语是“任意精度”,google用php.net/manual/en/book.bc.php
吉米·霍法(Jimmy Hoffa)2012年

1
@吉米·霍法(Jimmy Hoffa):精度通常不是您所需要的,可以精确地表示并使用小数部分的精度是。当然,您经常会发现两者结合在一起,但是例如decimalC#中的类型精度有限,但非常适合货币价值。
Michael Borgwardt 2012年

1
@MichaelBorgwardt是的,我忘记了有理类型,因为很多语言都没有它们。好决定。
吉米·霍法

在使用货币和旧代码进行了大量工作之后,我会说将其存储为整数并使用美分而不是美元。但是请注意,32个整数可以容纳的数量少得令人惊讶。4)32位整数如果不带符号且使用美分,则只能容纳2100万个。
Pieter B

顺便说一句,您的示例代码显示了发布的价格和折扣,这让我感到恐惧。希望它是如此简化,不会反映实际使用情况,但是从表面上看,您似乎信任浏览器通过回传一个隐藏字段或类似内容来告诉您一件商品的价格是多少。
Carson63000

Answers:


6

使用PHP / MySQL处理数字可能非常棘手。如果您使用十进制(10,2)并且您的数字更长或更高精度,它将被截断而不会出错(除非您为数据库服务器设置了正确的模式)。

要处理大值或高精度值,您可以使用BCMath之类的,它将允许您对大数进行基本运算并保持所需的精度。

我不确定您将要进行什么精确的计算,但是您还必须记住,如果您在整个过程中未使用适当的精度,则(0.22 * 0.4576)+(0.78 * 0.4576)将不等于0.4576。

MySQL中DECIMAL的最大大小为65,因此对于任何目的,它都应该足够。如果使用DECIMAL字段类型,则无论使用ORM还是仅使用普通的PDO / mysql(i),它都将作为字符串返回。

数据应如何存储在数据库中?列类型?尺寸?

所需的精度十足。如果使用汇率,则至少需要四位小数

正常的加,减数据应如何处理。乘法还是除法?

使用BCMath来保存,为什么使用float可能不是一个好主意

我应该什么时候取整值?舍入多少舍入(如果有)?

对于货币值,通常可以接受两个小数位,但是例如,如果您使用汇率,则可能需要更多。

处理大货币值和低货币值之间有区别吗?

很大程度上取决于您的意思。高精度处理数字之间肯定有区别。


9

一个简单的解决方法是将它们存储为整数。99.99存储为9999。如果此方法不起作用(并且有很多原因可能导致错误的选择),则可以使用Decimal类型。在mysql端http://dev.mysql.com/doc/refman/5.0/zh-CN/precision-math-decimal-changes.html。在PHP方面,我发现了这个/programming/3244094/decimal-type-in​​-php,这可能就是您想要的。

奖金问题:很难说。Orm将根据所选的数据类型工作。我想说,您可以在抽象方面做些事来帮助,但是这个特定问题不能仅通过迁移到ORM来解决。


1
FWIW,Drupal Commerce使用9999技巧来存储其价格。
Florian Margaine,2012年

1
“这有很多原因,这可能是一个错误的选择。”您是否愿意分享这种做法的一些问题?
Songo 2012年


@MichaelBorgwardt感谢您提供信息,Songo对您的延迟表示抱歉,飓风把我们带了出去:)
Ominus

3

我将尽我的经验:

我正在寻找的东西是:

数据应如何存储在数据库中?列类型?尺寸?

我一直在使用DECIMAL(10,2)mysql而没有问题(8个完整和2个小数== 99.999.999,99 ==巨大的金额),但这取决于您需要支付的费用范围。应格外小心(例如OS最大浮动值)。在小数部分,我使用2个值来避免截断或舍入值。赚钱的情况很少,您需要更多的小数点(在这种情况下,您需要确保用户将使用所有小数点,否则无用的数据)

正常的加,减数据应如何处理。乘法还是除法?

使用一种货币和一张汇率表(带有日期)。这样,您可以确保始终保存正确的金额。附加功能:保存完整值并创建包含计算结果的视图。这将帮助您即时确定价值

我应该什么时候取整值?舍入多少舍入(如果有)?

同样,取决于您的系统资金范围。始终以KISS的方式思考,除非您需要陷入货币兑换的混乱之中

处理大货币值和低货币值之间有区别吗?

根据您的操作系统和编程语言,您始终需要检查最大值和最小值


感谢您的回答,但是如果我必须处理类似$valueToBeStored= $a * $b;if $a$b都从数据库读取为小数的事情,我认为它们将double在PHP中转换为正确的代码吗?这会影响数字吗?
Songo 2012年

$a$b取自数据库?因此,在我的示例中,您不需要存储任何数据,$valueToBeStored因为您将始终拥有源$a$b数据。因此,您可以以编程方式处理函数中的值,或使用列结果创建mysql视图。这样,如果必须更改任何值,则不必担心修改多个位置(容易出错)
Alwin Kesler 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.