因为PHP中的浮点数据类型不准确,并且MySQL中的FLOAT占用的空间比INT多(并且不准确),所以我总是将价格存储为INT,在存储之前乘以100以确保我们有2个小数精确的地方。但是我认为PHP是行为不端的。示例代码:
echo "<pre>";
$price = "1.15";
echo "Price = ";
var_dump($price);
$price_corrected = $price*100;
echo "Corrected price = ";
var_dump($price_corrected);
$price_int = intval(floor($price_corrected));
echo "Integer price = ";
var_dump($price_int);
echo "</pre>";
产生输出:
Price = string(4) "1.15"
Corrected price = float(115)
Integer price = int(114)
我很惊讶。当最终结果低于预期值1时,我期待我的测试结果看起来更像:
Price = string(4) "1.15"
Corrected price = float(114.999999999)
Integer price = int(114)
这将证明浮点类型的不准确性。但为什么楼(115)返回114?
答案 0 :(得分:26)
尝试将其作为快速解决方法:
$price_int = intval(floor($price_corrected + 0.5));
您遇到的问题不是PHP的错,所有使用带有浮点算术的实数的编程语言都有类似的问题。
货币计算的一般经验法则是永远不要使用浮点数(既不在数据库中也不在脚本中)。您可以通过始终存储美分而不是美元来避免各种问题。分是整数,你可以自由地将它们加在一起,并乘以其他整数。无论何时显示数字,请确保在最后两位数字前插入一个点。
你得到114而不是115的原因是floor
向下舍入,朝向最接近的整数,因此楼层(114.999999999)变为114.更有趣的问题是为什么1.15 * 100是114.999999999而不是115原因是1.15不完全是115/100,但是它的数量要小得多,所以如果乘以100,你得到的数字会比115小一点。
以下是echo 1.15 * 100;
所做的更详细解释:
PHP打印令人惊讶的Corrected price = float(115)
(而不是114.999 ...)的原因是var_dump
不打印确切的数字(!),但它会打印舍入到{{的数字1}}(或n - 2
)个数字,其中n个数字是计算的精度。您可以轻松验证:
n - 1
如果要打印浮动,请记住:打印浮动时,并不总是看到所有数字。
另请参阅PHP float文档开头附近的重要警告。
答案 1 :(得分:4)
答案 2 :(得分:3)
PHP正在根据有效数字进行舍入。它隐藏了不准确性(第2行)。当然,当地板出现时,它不会更好地知道它并且一直停下来。
答案 3 :(得分:3)
也许这是这个“问题”的另一种可能的解决方案:
intval(number_format($problematic_float, 0, '', ''));
答案 4 :(得分:0)
如上所述这不是PHP本身的问题,它更多的是处理不能表示为有限浮点值的分数的问题,因此在舍入时会导致字符丢失。
解决方案是确保在处理浮点值时需要保持准确性 - 使用gmp函数或BC数学函数 - bcpow,bcmul等。问题将很容易解决。
而不是 $ price_corrected = $ price * 100;
使用$ price_corrected = bcmul($ price,100);