上次讲到PHP是如何解析大整数的,一笔带过了number_format的处理,再详细阅读该函数的源码,以下是小分析。

函数原型

string number_format ( float $number [, int $decimals = 0 ] )

string number_format ( float $number , int $decimals = 0 , string $dec_point = "." , string $thousands_sep = "," )

函数可以接受1、2、4个参数(具体可以看代码的实现)。

如果只提供第一个参数,number的小数部分会被去掉,并且每个千位分隔符都是英文小写逗号"," ;

如果提供两个参数,number将保留小数点后的位数到你设定的值,其余同楼上;

如果提供了四个参数,number 将保留decimals个长度的小数部分, 小数点被替换为dec_point,千位分隔符替换为thousands_sep

PHP_FUNCTION(number_format)

// number
// 你要格式化的数字
// num_decimal_places
// 要保留的小数位数
// dec_separator
// 指定小数点显示的字符
// thousands_separator
// 指定千位分隔符显示的字符
/* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_separator, string thousands_separator]])
Formats a number with grouped thousands */
PHP_FUNCTION(number_format)
{
// 期望number_format的第一个参数num是double类型的,在词法阶段已经对字面量常量做了转换
double num;
zend_long dec = 0;
char *thousand_sep = NULL, *dec_point = NULL;
char thousand_sep_chr = ',', dec_point_chr = '.';
size_t thousand_sep_len = 0, dec_point_len = 0;
// 解析参数
ZEND_PARSE_PARAMETERS_START(1, 4)
Z_PARAM_DOUBLE(num)// 拿到double类型的num
Z_PARAM_OPTIONAL
Z_PARAM_LONG(dec)
Z_PARAM_STRING_EX(dec_point, dec_point_len, 1, 0)
Z_PARAM_STRING_EX(thousand_sep, thousand_sep_len, 1, 0)
ZEND_PARSE_PARAMETERS_END();
switch(ZEND_NUM_ARGS()) {
case 1:
RETURN_STR(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr));
break;
case 2:
RETURN_STR(_php_math_number_format(num, (int)dec, dec_point_chr, thousand_sep_chr));
break;
case 4:
if (dec_point == NULL) {
dec_point = &dec_point_chr;
dec_point_len = 1;
}
if (thousand_sep == NULL) {
thousand_sep = &thousand_sep_chr;
thousand_sep_len = 1;
}
// _php_math_number_format_ex
// 真正处理的函数,在本文件第1107行
RETVAL_STR(_php_math_number_format_ex(num, (int)dec,
dec_point, dec_point_len, thousand_sep, thousand_sep_len));
break;
default:
WRONG_PARAM_COUNT;
}
}
/* }}} */

代码执行流程图

_php_math_number_format_ex

函数实现的各种参数数量,最终都会调用_php_math_number_format_ex函数。函数主要做的是:

处理负数;

根据要保留的小数点对浮点数进行四舍五入;

调用strpprintf函数将浮点数表达式转成字符串表示;

计算需要分配给结果变量的字符串长度;

将结果拷贝到返回值中(如果有千位符,则进行千位符分割)

strpprintf

这个函数是实现浮点数与字符串的转换,如上文所说,最终是调用了php_conv_fp函数做的转换(这里是通过gdb调试做的定位),而php_conv_fp函数,往下追踪,调用的是zend_dtoa函数,

更多细节注解,见github项目提交记录

总结

阅读完这个函数的源码,学习到的是浮动数与字符串的互相转换的实现细节,字符串与浮点数之间的关系较复杂,之后还要继续学习。

原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

更多精彩内容,请关注个人公众号。

[PHP源码阅读]number_format函数的更多相关文章

  1. [Redis源码阅读]sds字符串实现

    初衷 从开始工作就开始使用Redis,也有一段时间了,但都只是停留在使用阶段,没有往更深的角度探索,每次想读源码都止步在阅读书籍上,因为看完书很快又忘了,这次逼自己先读代码.因为个人觉得写作需要阅读文 ...

  2. [Redis源码阅读]dict字典的实现

    dict的用途 dict是一种用于保存键值对的抽象数据结构,在redis中使用非常广泛,比如数据库.哈希结构的底层. 当执行下面这个命令: > set msg "hello" ...

  3. [Redis源码阅读]redis持久化

    作为web开发的一员,相信大家的面试经历里少不了会遇到这个问题:redis是怎么做持久化的? 不急着给出答案,先停下来思考一下,然后再看看下面的介绍.希望看了这边文章后,你能够回答这个问题. 为什么需 ...

  4. linux源码阅读笔记 fork函数

    在阅读源码的过程中,发现找不到fork函数的定义.后来在linux/init/main.c中找到了这样一条语句 static inline _syscall0(int,fork) 原来这里就是fork ...

  5. linux源码阅读笔记 asm函数

    在linux源码中经常遇到__asm__函数.它其实是函数asm的宏定义 #define __asm__ asm,asm函数让系统执行汇编语句. __asm__常常与__volatile__一起出现. ...

  6. [PHP源码阅读]explode和implode函数

    explode和implode函数主要用作字符串和数组间转换的操作,比如获取一段参数后根据某个字符分割字符串,或者将一个数组的结果使用一个字符合并成一个字符串输出.在PHP中经常会用到这两个函数,因此 ...

  7. CI框架源码阅读笔记3 全局函数Common.php

    从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap ...

  8. 3 EventTime 事件时间类和TimeNow函数——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 这里是时间相关类的第三个部分,也是最后一个部分. EventTime 事件时间类 这个类和Dela ...

  9. PHP源码阅读笔记一(explode和implode函数分析)

    PHP源码阅读笔记一一.explode和implode函数array explode ( string separator, string string [, int limit] )此函数返回由字符 ...

随机推荐

  1. 通过 PHP 连接China Azure Blob 存储

    问题说明 Azure Blob 存储是一种将非结构化数据作为对象/Blob存储在云中的服务.Blob存储可以存储任何类型的文本或二进制数据,例如文档.媒体文件或应用程序安装程序.Blob存储也称为对象 ...

  2. 解决VS2015中没有报表项(ReportViewer)的方法

    作者:何时.微笑成了种奢求 VS2015中没有报表项(ReportViewer),怎么办?这篇文章主要为大家详细介绍了解决VS2015中没有报表项(ReportViewer)的方法,感兴趣的小伙伴们可 ...

  3. k-临近算法学习

    本章主要内容: k-临近算法是通过对象本身的特征将对象划分到某一类型中去,比如电影中的题材类型的分类是,可能就会考虑到电影中出现的镜头出现的次数的多少将电影划分为动作电影,爱情电影等等,本次的随笔参考 ...

  4. Redis使用记录-相关资料汇总

    1 redis在centos上的安装 http://www.cnblogs.com/hanyinglong/p/5036558.html 2 redis在windows上的可视化GUI工具 https ...

  5. 在JS事件封装时,addEventListener()方法的this问题

    最近在写js的类库,模仿的是jquery的编程风格,当封装到事件监听的时候发现遇到了一个问题,代码是这样的: 上面是封装的一个事件委托的代码,我以为上面的封装跟普通的事件监听一样简单,结果我在调用时发 ...

  6. 正则表达式 提取<A>标签

    功能用途 主要实现了提取html代码中的a标签和url地址. 示例代码 Regex regex = new Regex("href\\s*=\\s*(?:\"(?<1> ...

  7. SQL 软解析和硬解析详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt329 当客户端进程,将SQL语句通过监听器发送到Oracle时, 会触发一个 ...

  8. Redis 常用数据结构及其控制命令整合

    Redis 键值支持5种基本结构,分别是字符串,列表,哈希,集合,有序集合.每一种数据结构都有对应的取值和设值命令,辅助命令,除此之外,还有一些全局命令,用来管理Redis存储的所有 键. 全局命令 ...

  9. 浏览器事件window.onload、o…

    原文地址:浏览器事件window.onload.onfocus.onblur.onscroll和resize作者:lilyxiao <html> <head> <titl ...

  10. 自制EIGRP配置实验大全

    EIGRP综合实验 本实验可能会有一两个出错的地方,希望大家进行实验测试后,可以指正!当然绝大部分都是正确滴! 实验A:基本配置 A.0)实验目的: 熟悉eigrp的基本配置 A.1)实验拓扑: A. ...