本文主要介绍PHP浮点数你应该知道的知识,本文讲解了PHP浮点数,PHP数的临界值,精度损失等问题,有需要的朋友可以参考一下。
PHP 是弱类型语言,这个特性要求无缝透明的隐式类型转换,PHP 使用 zval 来存储任意类型的值,zval 的结构如下(以 5.2 为例):
代码如下:
{
/* */
值;/* 值 */
;
类型;/* 类型 */
;
};
在上面的结构中,联合实际上存储了值本身:
代码如下:
联合 {
long lval; /* 长值 */
dval; /* 值 */
{
字符*val;
int 长度;
} 字符串;
*ht; /* 哈希表值 */
对象;
};
今天我们只讨论两个成员,lval 和 dval。我们应该意识到 long lval 的长度是可变的,取决于编译器和操作系统的字长。可能是或,而 dval(双精度)是 IEEE 754 规定的固定长度。必须是。
请记住这一点,因为这会使一些 PHP 代码“不独立于平台”。在下面的讨论中,除非另有说明,我们假设 long 是
这里就不引用IEEE 754浮点数的表示方法了,有兴趣的可以自己去查一下,重点是的尾数是52位存储的,包括隐藏的1个有效位,一共是。
这里就引出了一个很有意思的问题,我们以C代码为例(假设long为):
代码如下:
长a=x;
(a == (long) ()a);
请告诉我,当a的值在什么范围内时,上面的代码可以断言成功?(答案在文章最后)
现在让我们回到正题。在 PHP 执行脚本之前,它首先需要读取脚本并对其进行分析。此过程还包括对脚本中的文字进行 zval 处理。例如,对于以下脚本:
代码如下:
$a = ; // 64 位有符号数的最大值
$b = ; // 最大值 + 1
($a);
($b);
输出:
代码如下:
int()
浮点(9.+18)
也就是说在词法分析阶段,PHP 会判断某个字面量的值是否超出当前系统的长表值范围,如果不超出,则存放在 lval,zval 中,否则以 dval,zval 来表示。
我们必须小心任何大于最大整数值的值,因为它可能会失去精度:
代码如下:
$a = ;
$b = ;
($a === ($b - 1));
输出为错误。
现在我们继续开始的讨论,前面提到过,PHP 整数可以是 32 位,也可以是 64 位,这就意味着一些在 64 位系统上可以正常运行的代码,可能会因为不可见的类型转换而丢失精度,导致代码无法在 32 位系统上正常运行。
所以,我们必须警惕这个临界值,幸运的是,PHP 中已经定义了这个临界值:
代码如下:
回声;
当然,为了安全起见,我们应该使用字符串来存储大整数,并使用诸如这样的数学库来执行计算。
另外还有一个可能让我们比较困惑的关键配置就是php,这个配置决定了PHP在输出浮点值的时候,输出多少位有效数字。
最后我们再回顾一下上面提出的问题,即长整型在转换为float型然后再转换回long型时,能够保证不损失精度的最大值是多少?
例如,对于整数,我们知道它的二进制表示形式为 101。现在,让我们将其右移两位至 1.01,丢弃高位隐含有效位 1,得到存储在中的二进制值 5:
代码如下:
0/*符号位*/ /*指数位*/
5 的二进制表示在尾数中保留完整,因此在这种情况下,转换回长整型时不会损失精度。
我们知道尾数用 52 位表示,并且包含隐含的前导 1,因此总精度为 53 位。所以我们还可以得出,如果长整数小于:
代码如下:
2^53 - 1 == 90991; //记住,我们现在假设它是一个长整数
那么,当发生 long->->long 值转换时,这个整数将不会丢失精度。
本文主要介绍PHP浮点精度问题总结,本文主要针对PHP浮点精度丢失问题,用三段文字从不同角度阐述了该问题产生的原因及解决方法,有需要的朋友可以参考下文
1.PHP浮点精度丢失问题
我们首先来看下面的代码:
代码如下:
$f = 0.57;
回声($f * 100);//56
结果可能会让你有点吃惊,PHP 遵循 IEEE 754 双精度:
浮点数具有 64 位双精度,由 1 个符号位 (E)、11 个指数位 (Q) 和 52 个尾数位 (M) 表示(共 64 位)。
符号位:最高位,表示数据的正值或负值,0表示正数,1表示负数。
指数位:表示数据是2的幂,指数用偏移码表示
尾数:表示数据小数点后的有效数字的位数。
让我们看一下小数在二进制中是如何表示的:
乘以2取整数,按顺序排列,即先将小数部分乘以2,再取整数部分,继续将剩余的小数部分乘以2,再取整数部分,将剩余的小数部分再乘以2,继续取小数部分。但是像0.57这样的小数,如果这样乘法,小数部分就不可能为0。有有效数字的小数,用二进制表示就是无限的。
0.57 的二进制表示基本上是(52位):
如果只有 52 位,则 0.57 => 0。
不难看出上述出乎意料的结果。
2. PHP 浮点数的精度
首先看一下问题:
代码如下:
$f = 0.58;
(($f * 100)); //为什么输出57呢?
相信很多同学都有过这样的疑问。
具体的原理可以看“鸟哥”的一篇文章,里面有详细的解释:PHP浮点数常见问题解答
那么如何避免这个问题呢?
方法有很多,这里介绍两种:
1.
代码如下:
(("%.10f",($a/$b)),0,-7);
2. 四舍五入(注意会进行四舍五入)
代码如下:
舍入($a/$b,3);
或者你还有更好的方法,也可以给我留言。
3. PHP浮点数常见问题解答
关于PHP的浮点数,我之前写过一篇文章:关于PHP中的浮点数全是“假”
然而,我忽略了一件事,也就是以下常见问题的答案:
代码如下:
$f = 0.58;
(($f * 100)); //为什么输出57呢?
为什么输出是 57?是 PHP 的 bug 吗?
我相信很多同学都有过这个疑问,因为有很多人问我类似的问题,更不用说经常有人问...
为了理解原因,我们首先需要知道浮点数的表示形式(IEEE 754):
例如,长度为 64 位(双精度)的浮点数将由 1 个符号位(E)、11 个指数位(Q)和 52 个尾数位(M)表示(共 64 位)。
符号位:最高位,表示数据的正值或负值,0表示正数,1表示负数。
指数位:表示数据是2的幂,指数用偏移码表示
尾数:表示数据小数点后的有效数字的位数。
这里重点是小数在二进制中的表示,具体小数如何表示可以百度一下,这里就不多说了,重点是0.58在二进制表示中是一个无穷大的值(以下数字都省略了隐含的1)。
0.58 的二进制表示基本上是(52位):
0.57 的二进制表示基本上是(52位):
两者的二进制如果仅通过这52位来计算的话,则是:
代码如下:
0.58 -> 0。
0.57 -> 0。
至于具体的浮点乘法 0.58 * 100,我们就不详细考虑了,有兴趣的可以看(point),我们只是做一个大概的心算...0.58 * 100 = 57。
那你自然就57了……
可以看出,这个问题的关键点是:“你看似有限的小数,在计算机的二进制表示中却是无限的”
所以,不要再认为这是一个 PHP 错误了,它就是这样的……
本文主要介绍PHP Hash算法:算法代码示例,本文直接给出了实现代码,需要的朋友可以参考一下。
最近在看一本书,里面提到了一些hash算法,印象最深的是当时还不是很理解,所以今天就写了个程序来验证一下。
首先是代码:
复制代码如下:
/**
* CRC32 哈希
* @参数$str
* @int
*/
($str)
crc32($str) >> 16 & ;
/**
*哈希
* @参数$str
* @int
*/
($str)
$哈希=0;
对于($i=0;$i
$hash += 33 * $hash + ord($str{$i});
$哈希&;
$n=10;
// 测试用例 1
$stat = 数组();
对于($i=0;$i
$str = (md5((true)), 0, 8);
$p = ($str) % $n;
如果(isset($stat[$p])){
$统计[$p]++;
}别的{
$统计[$p] = 1;
(统计)($stat);
// 测试用例 2
$stat = 数组();
对于($i=0;$i
$str = (md5((true)), 0, 8);
$p = ($str) % $n;
如果(isset($stat[$p])){
$统计[$p]++;
}别的{
$统计[$p] = 1;
(统计)($stat);
上面有两个测试用例,第一个是采用CRC32方法,第二个是算法实现。
影响:
结果显示两种算法不相上下(可能是数据来源的原因,md5只有0-f)。也有文章说CRC32分布更均匀(参考链接:)
但它很耗时,而CRC32的速度几乎是其两倍。
为什么是 33?
是质数,也是奇数,除了33,还有131,1313,5381等等,PHP内置的hash函数就是用5381,在“鸟哥”的一篇博文里也有提到。
扫一扫在手机端查看
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。