Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/241.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
php intval()和floor()返回值太低?_Php_Floating Accuracy_Rounding Error - Fatal编程技术网

php intval()和floor()返回值太低?

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

因为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_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, '', ''));