PHP-浮点数精度
结果为0.0099999998 怎么回事?我想知道为什么我的程序总是报告奇怪的结果 为什么PHP不返回预期的0.01?使用PHP的PHP-浮点数精度,php,floating-point,precision,floating-accuracy,Php,Floating Point,Precision,Floating Accuracy,结果为0.0099999998 怎么回事?我想知道为什么我的程序总是报告奇怪的结果 为什么PHP不返回预期的0.01?使用PHP的round()函数: 这个答案解决了问题,但不能解释原因。我认为这是显而易见的(我也是C++编程的,所以对我来说是显而易见的),但是如果不是,假设PHP有自己的计算精度,在那个特定的情况下它返回了关于该计算的最符合的信息。< /P> < P>因为0.01不能精确地表示为二进制序列的和的总和。这就是浮动存储在内存中的方式 我想这不是你们想听的,但这是对问题的回答。有关如
round()
函数:
这个答案解决了问题,但不能解释原因。我认为这是显而易见的(我也是C++编程的,所以对我来说是显而易见的),但是如果不是,假设PHP有自己的计算精度,在那个特定的情况下它返回了关于该计算的最符合的信息。< /P> < P>因为0.01不能精确地表示为二进制序列的和的总和。这就是浮动存储在内存中的方式
我想这不是你们想听的,但这是对问题的回答。有关如何修复,请参阅其他答案。因为浮点运算!=实数算术。对于某些浮动
a
和b
,(a+b)-b!=a
。这适用于任何使用浮动的语言
因为是有限精度的二进制数,所以有一个有限的数量,这导致了这样的惊喜。下面是另一个有趣的阅读:
回到你的问题上来,基本上没有办法精确地用二进制表示34.99或0.01(就像十进制一样,1/3=0.3333…),所以使用近似值代替。要解决此问题,您可以:
遗憾的是,PHP没有像do那样的十进制数据类型。浮点数字和所有数字一样,必须作为0和1的字符串存储在内存中。这都是计算机的一部分。浮点与整数的不同之处在于,当我们想要查看0和1时,我们如何解释它们 一位是“符号”(0=正,1=负),8位是指数(范围从-128到+127),23位是称为“尾数”(分数)的数字。所以(S1)(P8)(M23)的二进制表示的值是(-1^S)M*2^P “尾数”有一种特殊的形式。在正常的科学记数法中,我们显示“某人的位置”和分数。例如: 4.39 x 10^2=439 在二进制中,“某人的位置”是一个位。因为我们忽略了科学记数法中所有最左边的0(我们忽略了任何不重要的数字),所以第一位保证是1 1.101 x 2^3=1101=13 因为我们保证第一位是1,所以我们在存储数字时会删除该位以节省空间。所以上面的数字只存储为101(对于尾数)。假定为前导1 以二进制字符串为例
$a = '35';
$b = '-34.99';
echo ($a + $b);
将其分解为以下组件:
00000010010110000000000000000000
应用我们的简单公式:
Sign Power Mantissa
0 00000100 10110000000000000000000
+ +4 1.1011
+ +4 1 + .5 + .125 + .0625
+ +4 1.6875
换句话说,000000100101100000000000000000是27的浮点值(根据IEEE-754标准)
然而,对于许多数字,没有精确的二进制表示。很像1/3=0.333。。。。永远重复,1/100是0.0000001010001110101110000。。。。。重复“1010001110101110000”。然而,32位计算机不能以浮点形式存储整个数字。所以它做出了最好的猜测
(-1^S)M*2^P
(-1^0)(1.6875)*2^(+4)
(1)(1.6875)*(16)
27
(请注意,负7是使用2的补码生成的)
应该立即清楚,01111110010100011111101011100001010看起来与0.01完全不同
但是,更重要的是,它包含重复小数的截断版本。原始小数点包含重复的“1010001110101110000”。我们已将其简化为01000111101011100001010
通过我们的公式将浮点数转换回十进制,我们得到0.00999979(请注意,这是针对32位计算机的。64位计算机的精度要高得多)
十进制的等价物
如果它有助于更好地理解这个问题,那么在处理重复小数时,让我们看看十进制科学记数法
假设我们有10个“盒子”来存储数字。因此,如果我们想存储1/16这样的数字,我们会写:
0.0000001010001111010111000010100011110101110000
Sign Power Mantissa
+ -7 1.01000111101011100001010
0 -00000111 01000111101011100001010
0 11111001 01000111101011100001010
01111100101000111101011100001010
这显然是6.25e-2
,其中e
是*10^(
)的缩写。我们为十进制分配了4个框,尽管我们只需要2个(用零填充),我们为符号分配了2个框(一个用于数字的符号,一个用于指数的符号)
使用这样的10个框,我们可以显示从-9.9999e-9
到+9.9999e+9
这适用于任何小数点后4位或更少的数字,但是当我们尝试存储像2/3
这样的数字时会发生什么
+---+---+---+---+---+---+---+---+---+---+
| + | 6 | . | 2 | 5 | 0 | 0 | e | - | 2 |
+---+---+---+---+---+---+---+---+---+---+
这个新的数字0.66667
并不完全等于2/3
。事实上,它以0.000003333…
为准。如果我们尝试在基数3中写入0.66667
,我们将得到而不是0.2
如果我们使用较大的重复小数,例如1/7
,这个问题可能会变得更明显。它有6个重复数字:0.142857142857…
将其存储到十进制计算机中,我们只能显示其中的5位:
+---+---+---+---+---+---+---+---+---+---+
| + | 6 | . | 6 | 6 | 6 | 7 | e | - | 1 |
+---+---+---+---+---+---+---+---+---+---+
此编号为0.14286
,已被.000002857…
它“接近正确”,但它不是完全正确的,因此如果我们尝试将这个数字写在基数7中,我们会得到一些可怕的数字,而不是0.1
。事实上,将其插入Wolfram Alpha,我们会得到:
这些微小的分数差异对于您的0.00999979
(与0.01
)来说应该很熟悉。在这里可能很有用
+---+---+---+---+---+---+---+---+---+---+
| + | 1 | . | 4 | 2 | 8 | 6 | e | - | 1 |
+---+---+---+---+---+---+---+---+---+---+
(为了清晰起见,输出效率低下)
第一行给我0.00999999998。
第二个给了我0.01这里有很多关于浮点数为什么会这样工作的答案 但很少有人谈论武断的预演
<?PHP
$a = '35';
$b = '-34.99';
echo $a + $b;
echo '<br />';
echo bcadd($a,$b,2);
?>
$number = '12345678901234.1234567890';
$number2 = '1';
echo bcadd($number, $number2);
2250004.5000023
2250004.50000225
0.33333.
0.33333 = 3*10^-1 + 3*10^-2 + 3*10^-3 + 3*10^-4 + 3*10^-5.
$a = '35';
$b = '-34.99';
echo ($a + $b);
0.01 (decimal) = 0 10001111 01011100001010001111 (01011100001010001111)*(binary)
So 0.01 = 2^x + 2^y... 2^-z
0.01 * (2^(x+y+...z)) = (2^x + 2^y... 2^z)*(2^(x+y+...z)). This expression is true when (2^(x+y+...z)) = 100*x1. There are not integer n = x+y+...+z exists.
=> So 0.01 (decimal) must be infine in binary.