我在Magento
,Paybox
和SOAP
网络服务中付款时遇到了一个令人讨厌的错误,其想法如下:
Paybox
- API)我要做的是以下列方式转换我的订单价格:
$cents = $order->getBaseGrandTotal() * 100;
我还有一个Web服务SOAP(严格类型),respond
这个$cents
数量,但它会将其音乐化为(int)
,然后神奇的有时候转换的数量是不是预期的,转换后的结果不到一美分,在我的情况下可能是3736
。
$prices = array(39.8699, 12.3299, 11.3211);
foreach ($prices as $price) {
$stuff = round($price, 2) * 100;
echo $stuff . PHP_EOL;
}
echo "After int conversion" . PHP_EOL;
foreach ($prices as $price) {
$stuff = (int) (round($price, 2) * 100);
echo $stuff . PHP_EOL;
}
结果如下:
3987
1233
1132
After int conversion
3986
1233
1132
问题 有没有办法解决这个bug,它似乎是一个php bug?
答案 0 :(得分:1)
您的算法总结如下:
$price = 39.8699; // 39.869900000000001228
round($price, 2) * 100; // 3986.9999999999995453
(int)3986.9999999999995453; // 3986
除了最后一步,(int)
投射被截断之外,你到处都是正确的四舍五入。舍入会更合适:
round($price * 100)
说,根本问题是计算机使用二进制逻辑并且通常将数字存储为基数2,而我们人类使用模糊逻辑并且更喜欢基数10.“小”整数没有太多问题因为有1到1 1对应但是在固定大小的基数2表示中存储任意基数10浮点数通常只是近似值。一个典型的例子是 1.1 ,它在基数10中有两位数,但在基数2中是periodic:
1.0001100110011001100110011001100110011001100110011001100110011001101...
这就是为什么常见建议包括在可用时使用精确数据类型(在关系数据库中为DECIMAL
,在客户端代码中为整数)。