我在我的linux服务器上使用PHP 5.2.13。在舍入数字时,我遇到了奇怪的错误。这是我的测试用例:
<?php
echo " " . round(1.505, 2) . "\n";
echo " " . round(11.505, 2) . "\n";
echo " " . round(111.505, 2) . "\n";
echo " " . round(1111.505, 2) . "\n";
echo " " . round(11111.505, 2) . "\n";
echo " " . round(111111.505, 2) . "\n";
echo " " . round(1111111.505, 2) . "\n";
echo " " . round(11111111.505, 2) . "\n";
echo "" . round(111111111.505, 2) . "\n";
这是结果:
1.51
11.51
111.51
1111.51
11111.51
111111.51
1111111.5
11111111.51
111111111.51
有谁知道是什么原因引起的?我无法更新PHP,因为它是共享服务器。
答案 0 :(得分:10)
这是因为数字1111111.505无法用浮点表示法精确表示。它最接近的是1111111.5049999999。所以最终发生的是它将代码中的数字转换为1111111.50499999999,然后进行舍入。其中结果为1111111.5。浮点数有问题,因为它们不能完全准确地表示很多看似简单的十进制数。例如。使用二进制浮点数无法准确表示数字0.1。使用Python,如果输入0.1,则返回0.10000000000001。加或减几个零。这就是某些语言(如.Net)提供“十进制”数据类型的原因,该数据类型能够表示特定范围内的所有十进制值和小数位数。十进制数据类型的缺点是速度较慢,每个数字都需要更多空间来存储。
答案 1 :(得分:4)
如果仍然有人到达此页面时出现类似问题,其中浮点数减法会导致错误或奇怪的值。 我想用更多细节来解释这个问题。罪魁祸首是浮点数。 为了说明这个问题,我将用一个简单的例子来解释为什么你可以从那里假设它为什么会影响舍入等。
它与PHP没有直接关系,也不是bug。 但是,每个程序员都应该意识到这个问题。
这个问题在20年前甚至夺走了许多人的生命。
1991年2月25日,MIM-104爱国者导弹电池的浮动数计算问题使得它无法拦截沙特阿拉伯达兰的一枚来自飞毛腿的导弹,导致28名士兵死于美国陆军第14号军需官支队。
但为什么会这样呢?
原因是浮点值表示有限的精度。所以,价值可能 任何处理后都没有相同的字符串表示。它也是 包括在脚本中直接写入浮点值 在没有任何数学运算的情况下打印它。
只是一个简单的例子:
$a = '36';
$b = '-35.99';
echo ($a + $b);
你会期望它打印0.01,对吗? 但它会打印一个非常奇怪的答案,如0.009999999999998
与其他数字一样,浮点数double或float作为0和1的字符串存储在内存中。浮点与整数的不同之处在于我们在想要查看它们时如何解释0和1。它们的存储方式有很多标准。
浮点数通常作为符号位,指数字段和有效数字或尾数从左到右打包到计算机数据中....
由于空间不足,十进制数字没有很好地用二进制表示。所以,你不能完全表达1/3,就像它的0.3333333 ......,对吧?为什么我们不能将二进制浮点数表示为0.01也是出于同样的原因。 1/100为0.00000010100011110101110000 .....重复10100011110101110000。
如果0.01以二进制格式保存为01000111101011100001010的简化和系统截断形式,当它被转换回十进制时,它将被读取为0.0099999 ....取决于系统(64位计算机将提供比32位)。在这种情况下,操作系统决定是否按照它看到的方式打印它,或者如何以更人性化的方式进行打印。因此,它依赖于机器的方式来表示它。但它可以用不同的方法在语言层面进行保护。
如果格式化结果,则echo number_format(0.009999999999998,2);它将打印0.01。
答案 2 :(得分:3)
你需要升级wetwaer - 而不是PHP版本。
回声“”。回合(1.505,2)。 “\ n” 个;
您似乎认为您要求PHP返回1.505的舍入值 - 但实际上得到以返回1.505二进制版本的舍入版本的十进制版本
尝试输入数字here并查看二进制表示。
答案 3 :(得分:3)
对我来说,好像你遇到了accuracy problem caused by using floats。浮动不保证完全准确,您可能会发现此问题出现在一个系统上,而不是另一个系统上。
如果您真的关心任意浮点数的精度,请使用bcmath或gmp(如果有),但如果使用bcmath,则需要创建bcround()函数。我发现的唯一一个实际工作的是发布在php.net bcscale页面的评论中:
function bcround($number, $scale=0) {
$fix = "5";
for ($i=0;$i<$scale;$i++) $fix="0$fix";
$number = bcadd($number, "0.$fix", $scale+1);
return bcdiv($number, "1.0", $scale);
}
答案 4 :(得分:2)
@Shabbyrobe:你的功能是错误的:尝试用2的比例对这个数字进行舍入:44069.3445
它应该是44069.35,但默认是php round() - 你的函数返回44069.34
php.net成员的工作代码:
function mround($number, $precision=0) {
$precision = ($precision == 0 ? 1 : $precision);
$pow = pow(10, $precision);
$ceil = ceil($number * $pow)/$pow;
$floor = floor($number * $pow)/$pow;
$pow = pow(10, $precision+1);
$diffCeil = $pow*($ceil-$number);
$diffFloor = $pow*($number-$floor)+($number < 0 ? -1 : 1);
if($diffCeil >= $diffFloor) return $floor;
else return $ceil;
}
答案 5 :(得分:1)
Shabbyrobe的answer中的代码不处理负数。 这是对代码的有效改进,可以处理负数以及默认标度:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}