深入分析Php处理浮点数的问题

 更新时间:2016年11月25日 15:35  点击:2062
下文来为各位介绍Php处理浮点数的问题了,如果各位在使用过程中碰到这些问题我们可以一起来看看,希望文章对各位有帮助

公司要对产品价格做调整,因为做的外贸商城,所以价格要和国际接轨。比如国外的价格展示方式是:$35标识为$35.00; $56.2标识为:$56.20.

通过sprintf(“%1\$.2f”,$price)解决了上面的需求,但是新的问题出现,有价格为0的会处理为0.00.

通过empty()和判断是否相等,无法识别符点数0.00;通过百度总结了下面处理浮点数的方法。

浮点数0.00的处理

通过intval转换为整形intval(0.00) 变为0,只针对0.00使用intval处理;可以看先的例子你就明白了。

例子1:

$n=”19.99″;

print intval($n*100); //输出的结构是1998,而不是1999;

print intval(strval($n*100));//这个输出的才是1999;

例子二:

echo floor((0.1+0.7)*10);//输出的是7,而不是8;

echo floor(strval((0.1+0.7)*10));//这个才是8;

在php中一些简单的浮点数据在内部不能以精确的二进制来表示的。这和计算机的数据表示相关,即:不可能以有限的二进制来表示某些十进制的分数。永远不要相信浮点数的结果精确到了最后一位, 也永远不要比较两个浮点数是否相等。

上面的2个例子,总结出php处理浮点数的方式是将其转成字符串, 可以通过strval或者使用printf/sprintf将浮点数转成字符串.

浮点数精度

显然简单的十进制分数如同 0.1 或 0.7 不能在不丢失一点点精度的情况下转换为内部二进制的格式。这就会造成混乱的结果:例如,floor((0.1+0.7)*10) 通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9999999999...。

这和一个事实有关,那就是不可能精确的用有限位数表达某些十进制分数。例如,十进制的 1/3 变成了 0.3333333. . .。

优先级通常是讲运行算了,在php中各种运算符是非常的多了,在这里我就来为各位整理一份面试公司可能会常出的优先级例子,具体如下。


先看看题目

echo '1'.print(2)+3;

正确的结果应该是

511对于这个答案,我说“!@##¥%¥%……”,没办法答案确实没错。
那么我们来分析一下为什么会是这个答案,如标题所言这是一道坑爹的的优先级的PHP题目,那就按优先级的思路的分析(反推)。

1、先执行print

print(2)+3;//等同于print(2+3),这时缓冲区输出5。别问我为什么,手册里说的

2、print是一个函数,有返回结果,int类型

print(5);//结果等于1,这时候echo '1'.1,这个时候缓冲区又输出了11

3、最终的结果(按输出顺序):511

补充一些关于PHP优先级知识:

运算符优先级 


下表从低到高列出了运算符的优先级。 
  结合方向    运算符
  左      ,
  左      or
  左      xor
  左      and
  右      print
  右      = += -= *= /= .= %= &= |= ^=                      ~=   <<=  >>= 
  左      ? :
  左      ||
  左      &&
结合方向        运算符
  左      |
  左      ^
  左      &
  无      == != === !==
  无      <  <= > >= 
  左      <<  >>
  左      +  -  .
  左      *  /   %
  右      ! ~ ++ -- (int) (float) (string) (array) (object) @
  右      [
  无      new

cookie是一个存储在用户本地的一个小的文本文件了我们可以通过浏览器来访问它,下面我们来看看关于PHP 删除cookie的一些事项。


要删除 cookie 需要确保它的失效期是在过去,才能触发浏览器的删除机制

在php中,我们可以使用setcookie()函数来设置浏览器的Cookie信息。

常见的设置cookie的示例代码如下:

<?php

$name = 'mycookie'; // cookie名称
$value = 'CodePlayer'; // cookie值
$expire = time() + 3600 * 24 * 7; // 过期时间 7天
$path = '/'; // 设置可以使用该cookie的路径,'/'表示站点根目录,该目录及所有子目录中均可访问该cookie。

// 设置一个cookie
setcookie( $name, $value, $expire, $path );

?>

不过,我们想要删除Cookie信息的话,又该怎么办呢。php并没有提供另外一个专门用来删除Cookie的函数,而是直接使用setcookie()函数来删除Cookie信息,我们只需要将过期时间更改为当前时间之间的时间即可。

<?php
// 设置cookie已过期,浏览器即可删除该cookie。此时可以为任意值。
setcookie( 'mycookie', 'CodePlayer', time() - 3600, '/' );

// 或者

// 将过期时间直接设为0,表示1970-1-1(已经过期了),可以避免time()及数学运算的消耗
setcookie( 'mycookie', 'CodePlayer', 0, '/' );
?>

此外,我们还可以将Cookie的值设为空字符串(“”)或null,也可用来删除Cookie。

<?php
/* 删除cookie */

setcookie('mycookie', '');
// 或者
setcookie('mycookie', null);
?>

这样就完了吗?No!如果你直接如上使用空字符串或null的方式来删除Cookie,可能会导致对应的cookie无法删除。

当然,上述删除Cookie的方式是没有错的,错的是我们没有在删除Cookie时指定路径(第4个参数)。如果没有指定路径参数,则路径默认为当前请求URL所在目录。如果你设置Cookie时的路径与删除Cookie时的路径不一致,将无法删除该Cookie。

<?php
// 当前请求为:"/abc/cookie.php"

// 在路径"/"下设置cookie
setcookie( 'mycookie', 'CodePlayer', time() + 3600 * 24 * 7, '/' );

// 注意:这样删除是无效的,因为默认路径为当前目录,即:"/abc/"
// setcookie( 'mycookie', '');

// 删除路径"/"下设置的名为"mycookie"的cookie,此时时间值可随意,哪怕未过期也行
setcookie( 'mycookie', '', 0, '/');
?>

以下代码可以在php5.20的linux源码包中ext/standard/head.c第99行附近找到.

if (value && value_len == 0) {
/*
    * MSIE doesn't delete a cookie when you set it to a null value
    * so in order to force cookies to be deleted, even on MSIE, we
    * pick an expiry date 1 year and 1 second in the past
    */
time_t t = time(NULL) - 31536001;
dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, t, 0 TSRMLS_CC);
sprintf(cookie, "Set-Cookie: %s=deleted; expires=%s", name, dt);
efree(dt);
} else {
sprintf(cookie, "Set-Cookie: %s=%s", name, value ? encoded_value : "");
if (expires > 0) {
strcat(cookie, "; expires=");
dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, expires, 0 TSRMLS_CC);
strcat(cookie, dt);
efree(dt);
}
}

源码中清清楚楚的显示,if (value && value_len == 0) ,当value_len为0

sprintf(cookie, "Set-Cookie: %s=deleted; expires=%s", name, dt);

会发送删除cookie的http头给浏览器.

最后我们可以得出结论,在php中使用

setcookie($cookiename, '');或者 setcookie($cookiename, NULL);

都会删除cookie,当然这些手册中并没有。

面试题对于我们要去找工作的朋友多少都会要看一下的,这里一聚教程小伙伴为各位整理PHP类型转换的面试题与答案解析,希望本文章能够对各位有帮助。


最近在为公司面试新人,经常会问到的一道题目就是PHP类型转换的值,例如:
 
var_dump((int)true);
var_dump((string)true);
var_dump((string)false);
var_dump((bool)"1");
var_dump((bool)"0");
var_dump((bool)"");
var_dump((bool)"false");

我印象中最早见到这道题目是在英极的PHP高级开发工程师岗位的笔试题里面,看似很基础,但是依然可以难住不少PHPer。先来看一下运行结果:

int(1)
string(1) "1"
string(0) ""
bool(true)
bool(false)
bool(false)
bool(true)

对于大多数人来说,第1、2、4行通常是没有问题的。但是为什么false转换为字符串是空字符串呢?在处理请求值时,通常会传一个字符串类型的false,但是“false”(字符串)并非false(布尔),这有点令人疑惑了。

为什么会这样呢?

关于这个问题,我们从PHP内核入手,看看在类型转换时系统内部到底发生了什么。

首先补充一些关于PHP弱类型实现方式的背景知识。PHP解释器是使用C语言写成的,当然最终对变量的处理,也会使用C语言构造数据结构来实现。在Zend引擎中,一个PHP变量对应的类型是zval。

打开Zend/zend_types.h文件,我们可以看到zval类型的定义,php-5.5.23版本大约在第55行左右:

typedef struct _zval_struct zval;


这样我们发现,zval其实是一个名为_zval_struct的结构体类型,我们在Zend/zend.h文件中找到这个结构体的定义,大约在320行左右开始:


 
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
} zvalue_value;
 
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};

大家可以看到,_zval_struct中包含两个重要的成员,一个是zvalue_value类型的value,一个是zend_uchar类型的type。注意zvalue_value类型是一个联合体,它用来存储一个PHP变量的值的信息。(如果你忘记了什么是联合体,我来解释一下。联合体类似结构体,但是联合体的中的成员,存储时有且只能有一个,而且联合体占用的空间是联合体中长度最长的那个成员,这样做是为了节省内存的使用。)在zvalue_value中,包括了long、double、struct、HashTable、zend_object_value五个类型的成员。他们分别用来存储PHP变量不同类型的值:
C类型  PHP类型
long  bool
int
resource
double  float
struct  string
HashTable  array
zend_object_value  object

看到这个结构体之后,想必也就明白了常问的诸如PHP中int类型的取值范围,以及php中strlen的时间复杂度之类的问题。

由此可见,PHP的变量类型转换,或者说是弱类型实现,本质上是实现zval类型在不同类型之间的转换。除了完成zvalue_value的数值转换,还需要将_zval_struct中的type设置成当前变量的type类型。在Zend引擎中实现了convert_to_*系列函数完成这一转换,我们在Zend/zend_operators.c中可以看到这些转换函数,在大约511行左右,可以找到转换为布尔类型的函数:


 
ZEND_API void convert_to_boolean(zval *op) /* {{{ */
{
int tmp;
 
switch (Z_TYPE_P(op)) {
case IS_BOOL:
break;
case IS_NULL:
Z_LVAL_P(op) = 0;
break;
case IS_RESOURCE: {
TSRMLS_FETCH();
 
zend_list_delete(Z_LVAL_P(op));
}
/* break missing intentionally */
case IS_LONG:
Z_LVAL_P(op) = (Z_LVAL_P(op) ? 1 : 0);
break;
case IS_DOUBLE:
Z_LVAL_P(op) = (Z_DVAL_P(op) ? 1 : 0);
break;
case IS_STRING:
{
char *strval = Z_STRVAL_P(op);
 
if (Z_STRLEN_P(op) == 0
|| (Z_STRLEN_P(op)==1 && Z_STRVAL_P(op)[0]=='0')) {
Z_LVAL_P(op) = 0;
} else {
Z_LVAL_P(op) = 1;
}
STR_FREE(strval);
}
break;
case IS_ARRAY:
tmp = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0);
zval_dtor(op);
Z_LVAL_P(op) = tmp;
break;
case IS_OBJECT:
{
zend_bool retval = 1;
TSRMLS_FETCH();
 
convert_object_to_type(op, IS_BOOL, convert_to_boolean);
 
if (Z_TYPE_P(op) == IS_BOOL) {
return;
}
 
zval_dtor(op);
ZVAL_BOOL(op, retval);
break;
}
default:
zval_dtor(op);
Z_LVAL_P(op) = 0;
break;
}
Z_TYPE_P(op) = IS_BOOL;
}
/* }}} */

case IS_STRING这段代码即是将一个字符串类型变量转换为布尔型的操作。可以看到,只有空字符串,或者字符串长度为1,并且此字符为0时,字符串的布尔值才为1,也就是true,其他为0,也就是false。

同样的,我们也就明白了布尔值如何转换为字符串的,可以从_convert_to_string函数的实现中了解。

看似简单并且基础的PHP问题,究其根源是对PHP实现机制的把握。个人觉得,这道题也不失为鉴别PHPer知识边界的一道好题目。

本文我们来分享一段可以同时ping多个ip然后对比找出网络最快的ip的php程序,这段程序用来找代理ip可是神器。


为了翻墙方便 ,买了个vpn,转到osx下面官方没有提供合适的客户端,无法选择最快的线路。于是就自己写了个脚本,手动ping出最快的IP.

 代码如下 复制代码
$servers = array(
    array('VIP荷兰线路01','nl01.yyuu.me','81.4.105.195','4122或3108'),
    array('VIP法国线路01','fr01.yyuu.me','176.31.206.242','4122或3108'),
    array('VIP意大利线路01','it01.yyuu.me','37.247.48.226','4122或3108'),
    array('VIP新加坡线路01','sg01.yyuu.me','128.199.69.209','4122或3108')
);


//配合pcntl_signal使用
declare(ticks=1);
//最大的子进程数量
$max = count($servers);
//当前的子进程数量
$child = 0;

//当子进程退出时,会触发该函数
function sig_handler($sig) {
    switch($sig) {
        case SIGCHLD:
            //do something
    }
}

//注册子进程退出时调用的函数
pcntl_signal(SIGCHLD, "sig_handler");
foreach($servers as $server){
    $pid = pcntl_fork();
    if($pid){
        //echo 'main thread start',PHP_EOL;
    }else{
        exec('ping -c 1 '.$server[2],$result);
        echo $server[1].' '.$server[2].' '.$result[1],PHP_EOL;
        exit();
    }
}
[!--infotagslink--]

相关文章

  • MYSQL事务回滚的2个问题分析

    因此,正确的原子操作是真正被执行过的。是物理执行。在当前事务中确实能看到插入的记录。最后只不过删除了。但是AUTO_INCREMENT不会应删除而改变值。1、为什么auto_increament没有回滚?因为innodb的auto_increament的...2014-05-31
  • Mysql索引会失效的几种情况分析

    索引并不是时时都会生效的,比如以下几种情况,将导致索引失效: 1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)  注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引 ...2014-06-07
  • go浮点数转字符串保留小数点后N位的完美解决方法

    这篇文章主要介绍了go浮点数转字符串保留小数点后N位解决办法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-05-11
  • python 爬取京东指定商品评论并进行情感分析

    本文主要讲述了利用Python网络爬虫对指定京东商城中指定商品下的用户评论进行爬取,对数据预处理操作后进行文本情感分析,感兴趣的朋友可以了解下...2021-05-28
  • Underscore源码分析

    Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象。这篇文章主要介绍了underscore源码分析相关知识,感兴趣的朋友一起学习吧...2016-01-02
  • PHP浮点数精度丢失问题解决方案

    浮点数就是有很我小数的那种并且不只单纯了数字了,而小编在用支付接口时就碰到浮点数丢失的问题,下文一起来看看问题解决方法. 先看下面这段代码: $f = 0.57; echo...2016-11-25
  • Google会不会取消PR的理由分析

    Google是这样介绍PageRank的:   Google 出类拔萃的地方在于专注开发“完美的搜索引擎”,联合创始人拉里&middot;佩奇将这种搜索引擎定义为可“确解用户...2017-07-06
  • Fatal error: Cannot redeclare class 原因分析与解决办法

    我使用的都是php __autoload状态自动加载类的,今天好好的程序不知道怎么在运行时提示Fatal error: Cannot redeclare class 了,看是重复定义了类,下面我来分析一下解决办...2016-11-25
  • 关于PHP文件包含一些漏洞分析

    文章简单的分析了在php文件包含时inlcude的一个漏洞分析,下面希望对大家有点用处哦。 基本的文件包含漏洞: 代码如下 复制代码 <?php include...2016-11-25
  • php echo print print_r三者区别分析

    php教程 echo print print_r三者区别分析 echo是PHP语句, print和print_r是函数,语句没有返回值,函数可以有返回值(即便没有用) print() 只能打印出简单类型变量的...2016-11-25
  • www.111cn.net 排名大跌分析降权原因

    我的主题爱这个站以前排名非常好,后来由于自己操作不当至今还没恢复到以后的那种好的排名,希望各位站长吸取点教训哦。 下面以我们http://www.111cn.net 为例吧,做SE...2016-10-10
  • PHP静态分析与跨站脚本检测

    最近在看PHP静态分析与跨站脚本检测的东西,用的是维也纳大学一个博士生做出来的Pixy,这个东西是开源的,而且也作了好几年了,功能逐渐增强。现在这个3.0.3版本里...2016-11-25
  • 关于web注册页的可用性分析

      在Nielsen的可用性工程里提到可用性的其中一个原则是Errors,记得刚开博客的时候也穷举了一些关于可用性方面的文章,里面将这个errors翻译成了“少错”,实际上...2016-09-20
  • 基于C++浮点数(float、double)类型数据比较与转换的详解

    本篇文章是对C++中浮点数(float、double)类型数据比较与转换进行了详细的分析介绍,需要的朋友参考下...2020-04-25
  • PHP session并发及session读写锁分析

    关于PHP session并发及session读写锁问题估计各大程序员都不会想到这个问题,因为一般情况我们不会使用session来做并发操作了,但有时也有可能用到,下面整理一个session并...2016-11-25
  • php FILTER_VALIDATE_FLOAT 浮点数验证

    filter_validate_float 过滤器把值作为浮点数来验证。 */ $var=12.3; var_dump(filter_var($var, filter_validate_float)); //float(12.3) /* 非负浮点数(正浮点数...2016-11-25
  • 浮点数在计算机中存储方式是怎样的

    这篇文章介绍了浮点数在计算机中是如何存储的,讲解的比较详细,有需要的朋友可以参考一下。...2020-04-25
  • 深入C++浮点数无效值定义与判定的解决办法

    本篇文章是对C++中浮点数无效值定义与判定进行了介绍,需要的朋友参考下...2020-04-25
  • 深入分析PHP const与define使用区别

    const是用于类成员常量的定义了,定义之后不可改,而define我们定义的是全局常量了, 这样我们在其它地方访问但不能改变了,具体还有一些细节我们下面给各位列出来吧 注...2016-11-25
  • golang 比较浮点数的大小方式

    这篇文章主要介绍了golang 比较浮点数的大小方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-05-01