我有一个小型的财务应用程序,PHP作为前端,MySQL作为后端。我有古老的偏见,我将钱存储在MySQL中作为整数美分。我的HTML表单允许输入美元值,如“156.64”,我使用PHP将其转换为美分,然后将美分存储在数据库中。
我有一个函数可以从表单中清除美元值,并将其转换为美分。我删除了前导文本,我删除了尾随文本,我乘以100并转换为整数。最后一步是
$cents = (integer) ($dollars * 100);
这几乎适用于所有事情,除了像“156.64”这样的极少数值,它们一直转换为15663美分。为什么这样做?
如果我这样做:
$cents = (integer) ($dollars * 100 + 0.5);
然后它一直有效。为什么我需要添加舍入值?
另外,我对不再需要以整数而不是浮点值存储金额的偏见?现代浮动计算是否会产生足够圆润和准确的货币价值,足以保证100%准确的会计处理?
答案 0 :(得分:4)
如果您需要精确度,则应使用MySQL中的DECIMAL数据类型存储货币值。
答案 1 :(得分:2)
你对浮动的“偏见”永远不会被克服 - 这是他们工作方式的基础。没有太多细节,他们存储一个基于2的幂的数字,因为不是所有十进制数都可以这样呈现,它并不总是有效。您唯一可靠的解决方案是将数字存储为数字序列和小数点的位置(根据上面提到的DECIMAL类型)。
我不是百分之百的PHP,但是乘法是否有可能将整数转换成浮点数,从而准确地引入了你试图避免的问题?
答案 2 :(得分:2)
货币/货币价值绝不应作为浮动存储在数据库中(或在程序中使用)。
您的整数方法很好,就像使用DECIMAL
,NUMERIC
或MONEY
类型一样。
你的问题是由美元被视为浮动而导致PHP并没有更好的类型来处理钱。根据分配$ dollar的时间,它可以被视为字符串或浮点数,但如果它仍然是* 100操作的字符串,如果它看起来像浮点数,则肯定会转换为浮点数。
你可能最好自己将字符串解析为整数“money”值(使用正则表达式),而不是依赖于PHP正在进行的隐式转换。
答案 3 :(得分:2)
投射不是round()
,而是圆形到最近,而truncates是十进制:(int)3.99
会产生3
。 (int)-3.99
会产生-3
。
由于浮点算法经常会导致错误(可能不会出现你想要的方向),如果你想要可靠的舍入,请使用round()
。
答案 4 :(得分:2)
您发布的代码首先执行乘法,在将值转换为整数之前强制执行引入错误的浮点计算。相反,您应该完全通过反转顺序来避免浮点运算。首先转换为整数值,然后执行算术。
假设先前的代码已经过验证并格式化了输入,请尝试:
list($bills, $pennies) = explode('.', $dollars);
$cents = 100 * $bills + $pennies;
您对浮点值的偏见代表金钱是有充分根据的,因为截断并且因为价值从基数10转换为基数2并再次转回。
答案 5 :(得分:1)
您永远不应该以浮点形式存储货币,因为它始终会得到您不期望的结果。
查看php BC Maths,它允许您将货币存储为字符串,然后对它们执行非常高精度的算术运算。
答案 6 :(得分:0)
而不是使用
$ cents =(整数)($ dollar * 100);
您可能想尝试使用:
$ cents = bcmul($ dollars,100,2);
答案 7 :(得分:0)
从float转换为整数时,数字将向舍入为零(src)。
答案 8 :(得分:0)
如果通过浮点运算输入货币(没有双关语),将货币存储为整数是没有意义的。如果您想从string
转换为int
并与您的“偏见”保持一致,您只需使用字符串函数。
您可以使用任意精度库除以10(它们在内部将数字作为字符串处理),例如bcdiv()或gmp_div_q(),但当然,您也可以从头开始使用它进行所有数学运算。
或者您可以使用纯字符串函数:
<?php
// Quick ugly code not fully tested
$input = '156.64';
$output = NULL;
if( preg_match('/\d+(\.\d+)?/', $input) ){
$tmp = explode('.', $input);
switch( count($tmp) ){
case 1:
$output = $tmp[0];
break;
case 2:
$output = $tmp[0] . substr($tmp[1], 0, 2);
break;
default:
echo "Invalid decimal\n";
}
}else{
echo "Invalid number\n";
}
var_dump($output);
?>