PHP money string转换为整数错误

时间:2008-10-11 01:59:36

标签: php currency casting

我有一个小型的财务应用程序,PHP作为前端,MySQL作为后端。我有古老的偏见,我将钱存储在MySQL中作为整数美分。我的HTML表单允许输入美元值,如“156.64”,我使用PHP将其转换为美分,然后将美分存储在数据库中。

我有一个函数可以从表单中清除美元值,并将其转换为美分。我删除了前导文本,我删除了尾随文本,我乘以100并转换为整数。最后一步是

$cents = (integer) ($dollars * 100);

这几乎适用于所有事情,除了像“156.64”这样的极少数值,它们一直转换为15663美分。为什么这样做?

如果我这样做:

$cents = (integer) ($dollars * 100 + 0.5);

然后它一直有效。为什么我需要添加舍入值?

另外,我对不再需要以整数而不是浮点值存储金额的偏见?现代浮动计算是否会产生足够圆润和准确的货币价值,足以保证100%准确的会计处理?

9 个答案:

答案 0 :(得分:4)

如果您需要精确度,则应使用MySQL中的DECIMAL数据类型存储货币值。

答案 1 :(得分:2)

你对浮动的“偏见”永远不会被克服 - 这是他们工作方式的基础。没有太多细节,他们存储一个基于2的幂的数字,因为不是所有十进制数都可以这样呈现,它并不总是有效。您唯一可靠的解决方案是将数字存储为数字序列和小数点的位置(根据上面提到的DECIMAL类型)。

我不是百分之百的PHP,但是乘法是否有可能将整数转换成浮点数,从而准确地引入了你试图避免的问题?

答案 2 :(得分:2)

货币/货币价值绝不应作为浮动存储在数据库中(或在程序中使用)。

您的整数方法很好,就像使用DECIMALNUMERICMONEY类型一样。

你的问题是由美元被视为浮动而导致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)。

阅读Floating point precision警告。

答案 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);

?>