php intval()和floor()返回值太低?
因为PHP中的float数据类型不准确,MySQL中的float比INT占用更多的空间(而且不准确),所以我总是将价格存储为INT,在存储之前乘以100,以确保精确到小数点后2位。然而,我认为PHP是行为不端的。示例代码:php intval()和floor()返回值太低?,php,floating-accuracy,rounding-error,Php,Floating Accuracy,Rounding Error,因为PHP中的float数据类型不准确,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_du
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>";
我很惊讶。当最终结果低于预期值1时,我希望测试的输出看起来更像:
$price_int = intval(floor($price_corrected + 0.5));
这将证明浮动类型的不精确性。但为什么地板(115)返回114°请尝试以下快速修复方法:
echo 1.15 * 100; # this prints 115
printf("%.30f", 1.15 * 100); # you 114.999....
echo 1.15 * 100 == 115.0 ? "same" : "different"; # this prints `different'
echo 1.15 * 100 < 115.0 ? "less" : "not-less"; # this prints `less'
您遇到的问题不是PHP的错,所有使用实数和浮点运算的编程语言都有类似的问题
货币计算的一般经验法则是永远不要使用浮动(无论是在数据库中还是在脚本中)。您可以通过始终存储美分而不是美元来避免各种问题。美分是整数,您可以自由地将它们相加,然后乘以其他整数。无论何时显示数字,请确保在最后两位数字前插入一个点
之所以得到114而不是115,是因为floor
向下舍入到最接近的整数,因此floor(114.99999999)变为114。更有趣的问题是为什么1.15*100是114.9999999而不是115。原因是1.15并不完全是115/100,但它要小一点,所以如果你乘以100,你会得到一个比115小一点的数字
这里是一个更详细的解释什么echo 1.15*100代码>是否:
- 它将1.15解析为二进制浮点数。这涉及到舍入,它恰好向下舍入一点点,以得到最接近1.15的二进制浮点数。无法获得精确数字(无舍入误差)的原因是1.15在基数2中有无穷多的数字
- 它将100解析为二进制浮点数。这涉及舍入,但由于100是一个小整数,舍入误差为零
- 它计算前两个数字的乘积。这还需要一点舍入,以找到最接近的二进制浮点数。在此操作中,舍入误差恰好为零
- 它将二进制浮点数转换为带点的以10为基数的十进制数,并打印此表示形式。这也涉及到一点舍入
PHP打印出人意料的Corrected price=float(115)
(而不是114.999…)的原因是var\u dump
不打印准确的数字(!),而是打印四舍五入到n-2
(或n-1
)位的数字,其中n位是计算的精度。您可以轻松验证这一点:
intval(number_format($problematic_float, 0, '', ''));
echo 1.15*100;#这张是115
printf(“%.30f”,1.15*100);#你是114.999。。。。
回声1.15*100==115.0?“相同”:“不同”这印的是“不同的”
回声1.15*100<115.0?“更少”:“不更少”#这印的“少”
如果您正在打印浮点数,请记住:打印浮点数时并不总是看到所有数字
另请参见文档开头附近的大警告。PHP正在根据有效数字进行舍入。它隐藏了错误(在第2行)。当然,当地板出现时,它不知道有什么更好的办法,会一路往下压。我相信,其他答案已经涵盖了问题的原因和一个很好的解决方法
要从不同角度解决问题,请执行以下操作:
对于在MySQL中存储价格值,您可能应该查看,它允许您使用小数位存储精确的值。也许这是解决此“问题”的另一种可能的解决方案:
如前所述,这不是PHP本身的问题,更多的是处理不能表示为有限浮点值的分数的问题,因此在四舍五入时会导致字符丢失
解决方案是确保在处理浮点值时,需要保持精度-使用gmp函数或BC数学函数-bcpow、bcmul等。问题将很容易解决
代替
$price_corrected=$price*100
使用$price\u corrected=bcmul($price,100) $price_int=intval(floor($price_corrected+0.5))与$price_int=intval(round($price_corrected))相同。@pts,混沌是正确的,实际上我的原始代码是这样的。没有区别。我的问题是,为什么PHP说$price_转换为115,而实际上是114.999999?(我明白为什么是114.99999…@pts,我说混沌是正确的是错的,我误读了你的代码。那可能行。好的,这行。但是,+0.5或使用round()哪个更好?使用round()的含义是什么?使用round(),因为它是自包含且更干净的。+0.5可能会引入更多舍入。这实际上与PHP无关,更多的是与所有计算机如何处理浮点数据有关。如果您以前没有遇到过浮点不准确的情况,您可能想阅读有关该主题的文章。@jmucchiello,我不同意。我确实了解计算机如何处理浮点数据,这就是为什么我使用整数数据来代替。这是一个PHP问题,当底层数据明显为114.9999999…+1时,PHP显示为115,但我要补充的是,我不确定mySQL中的浮点类型有多“不准确”。它比执行任何额外的乘法运算要精确得多,并且让您的数据处于更好的状态以供分析。不管怎样,他说的是。@chadbirch,INT使用的空间不是比DECIMAL少很多吗?我总是发现INT+逻辑知道小数位数的偏移量比十进制更有效。特别是像价格这样的东西。实际上,该列是price_in_centsNo,除非小数位数很多。根据手册,INT使用4个字节(假设您使用的是实际的INT和
echo 1.15 * 100; # this prints 115
printf("%.30f", 1.15 * 100); # you 114.999....
echo 1.15 * 100 == 115.0 ? "same" : "different"; # this prints `different'
echo 1.15 * 100 < 115.0 ? "less" : "not-less"; # this prints `less'
intval(number_format($problematic_float, 0, '', ''));