Fork me on GitHub

标签 php 下的文章

【转】PHP浮点数问题

作者: Laruence( )
本文地址: http://www.laruence.com/2013/03/26/2884.html
转载请注明出处

关于PHP的浮点数, 我之前写过一篇文章: 关于PHP浮点数你应该知道的(All ‘bogus’ about the float in PHP)

不过, 我当时遗漏了一点, 也就是对于如下的这个常见问题的回答:

    <?php
        $f = 0.58;
        var_dump(intval($f * 100)); //为啥输出57
    ?>

为啥输出是57啊? PHP的bug么?

我相信有很多的同学有过这样的疑问, 因为光问我类似问题的人就很多, 更不用说bugs.php.net上经常有人问…

要搞明白这个原因, 首先我们要知道浮点数的表示(IEEE 754):

浮点数, 以64位的长度(双精度)为例, 会采用1位符号位(E), 11指数位(Q), 52位尾数(M)表示(一共64位).

符号位:最高位表示数据的正负,0表示正数,1表示负数。

指数位:表示数据以2为底的幂,指数采用偏移码表示

尾数:表示数据小数点后的有效数字.

这里的关键点就在于, 小数在二进制的表示, 关于小数如何用二进制表示, 大家可以百度一下, 我这里就不再赘述, 我们关键的要了解, 0.58 对于二进制表示来说, 是无限长的值(下面的数字省掉了隐含的1)..

0.58的二进制表示基本上(52位)是: 0010100011110101110000101000111101011100001010001111
0.57的二进制表示基本上(52位)是: 0010001111010111000010100011110101110000101000111101

而两者的二进制, 如果只是通过这52位计算的话,分别是:

0.58 -> 0.57999999999999996
0.57 -> 0.56999999999999995

至于0.58 100的具体浮点数乘法, 我们不考虑那么细, 有兴趣的可以看(Floating point), 我们就模糊的以心算来看… 0.58 100 = 57.999999999

那你intval一下, 自然就是57了….

可见, 这个问题的关键点就是: “你看似有穷的小数, 在计算机的二进制表示里却是无穷的”

so, 不要再以为这是PHP的bug了, 这就是这样的…..

PHP 嵌入式 SAPI

  php构建了一个层级系统. 最高层是提供用户空间函数和类库的所有扩 展. 同时, 其下是服务API(SAPI)层, 它扮演了webserver(比如apache, iis以及命令行接口 cli)的接口.在这许多sapi实现中有一个特殊的sapi就是嵌入式sapi.
  当这个sapi实现被构建时, 将 会创建一个包含所有你已知的php和zend api函数以及变量的库对象, 这个库对象还包含一些额外的帮助函数和宏, 用以简化外部程序的调用.


- 阅读剩余部分 -

PHP字符串中的转义

最近用字符串拼装正则表达式的时候,发现转义时加一条反斜杠与两条的效果是一样的

$pattern = "\d";
$pattern = "\\d";
preg_match('/'.$pattern.'/', '123', $matches);

非常不解,在查阅资料和多次测试后得出结论如下:

反斜杠在PHP的两种字符串(单引号定界符、双引号定界符)中,都具有转义效果,但是它们支持的转义字符是不同的。

单引号为定界符的php字符串,支持两个字符的转义

    \' 单引号 ( $var = '\''; )

    \\ 反斜杠 ( $var = '\\'; )

双引号为定界符的php字符串,支持下列字符的转义:

    \\ 反斜杠 ( $var = "\\"; )

    \" 双引号 ( $var = "\""; )

    \
 换行(LF 或 ASCII 字符 0x0A(10))  
    \
 回车(CR 或 ASCII 字符 0x0D(13))  
    \t 水平制表符(HT 或 ASCII 字符 0x09(9))  

    \$ 美元符号 

在字符串中,当未被转义的反斜杠后面的字符支持转义时,这个反斜杠后面的字符就会被转义。

例如:

<?php
    $str = '\\';    //var_dump($str)将输出:string(1) "\"
    $str = '\a';    //var_dump($str)将输出:string(2) "\a"
    $str = '\\a';    //var_dump($str)将输出:string(2) "\a"
    $str = '\\\a';    //var_dump($str)将输出:string(3) "\\a"

PHP的Curl类

经常会用到PHP cURL 模拟一些http请求,每次都写一大段很不方便,

所以在github上找到一个PHP Curl类,https://github.com/php-curl-class/php-curl-class

文档地址:http://www.crarun.com/docs/curl/curl.html

Quick Start and Examples

require 'Curl.php';
use CurlCurl;
 
//GET请求
$curl = new Curl();
$curl->get('
 
//POST请求
$curl = new Curl();
$curl->post('http://www.example.com/login/', array(
    'username' => 'myusername',
    'password' => 'mypassword',));
 
//带参数
$curl = new Curl();
$curl->setBasicAuthentication('username', 'password');
$curl->setUserAgent('');$curl->setReferrer('');
$curl->setHeader('X-Requested-With', 'XMLHttpRequest');
$curl->setCookie('key', 'value');
$curl->get(' 
if ($curl->error) {
    echo 'Error: ' . $curl->error_code . ': ' . $curl->error_message;
}else {
    echo $curl->response;
}
var_dump($curl->request_headers);
var_dump($curl->response_headers);

分页功能的实现

分页功能的实现需要三个基本要素:

pageCount(总页数),

currentPage(当前页码),

maxButtons(最大显示按钮数量)

根据这三个基本要素计算出要显示的页码范围,要求页码范围是有效的取值(假如页码从0开始计数,那页码范围就是区间[0,pageCount-1]的子集),php实现如下:


/**
 * 计算页码的范围(页码从0开始)
 */
protected function getPageRange()
{
    $currentPage=$this->getCurrentPage();
    $pageCount=$this->getPageCount();

    //先计算出有效的起点
    $beginPage=max(0, $currentPage-(int)($this->max_button_number/2));
    //再依据有效起点和最大显示按钮数量算出终点
    $endPage=$beginPage+$this->max_button_number-1;
    //验证终点的有效性
    if($endPage>=$pageCount)
    {
        //如果右边终点无效,则重新获取有效的终点
        $endPage=$pageCount-1;
        //根据有效终点和最大显示按钮算出有效起点
        $beginPage=max(0,$endPage-$this->max_button_number+1);
    }
    return array(
        'beginPage'=>$beginPage,
        'endPage'=>$endP
    );
}

这样就能保证maxButtons有效性。

上一页/下一页的显示逻辑:如果当前页不是第一页就显示上一页按钮,如果当前页不是最后一页就显示下一页按钮