上次讲到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. 【win10】大水牛主机插入耳机没有声音

    主机:大水牛,技嘉主板 操作系统:win10 问题:主机前面插入耳机,没有声音,扬声器图标出错 解决 一..插入耳机 二..Realtek高清晰音频管理器 1.打开音频管理器,点击右下角的设置 2.点 ...

  2. Mysql数据库防SQL注入原理

    每个语言都有自己的数据库框架或库,无论是哪种语言,哪种库,它们在数据库防注入方面使用的技术原理无外乎下面介绍的几种方法. 一.特殊字符转义处理 Mysql特殊字符指在mysql中具有特殊含义的字符,除 ...

  3. SpringMVC基础-@RequestMapping

    @RequestMapping @RequestMapping是一个用来处理请求地址映射的注解 可用于类或方法上.   类定义处:提供初步的请求映射信息.相当于当前 WEB 应用的根目录   方法处: ...

  4. 工具类:关于解决数据库中的日期格式,经过response.getWriter().write(json)打到前台日期格式混乱的问题的总结

    经过response.getWriter().write(json)打到前台日期格式混乱的问题的总结 import java.text.SimpleDateFormat;import net.sf.j ...

  5. spring整合mybatis错误:HTTP Status 404 - xxx-xxx....

    运行环境:jdk1.7.0_17 + tomcat 7 + spring 3.2.0 +mybatis 3.2.7+ eclipse,访问路径:http://localhost:8085/Spring ...

  6. visual studio xamarin 离线安装文件以及 android 模拟器

    介绍 为了使用vs开发android我也是煞费苦心,先是从网上各种搜刮文章,然后找各种各样的离线包(因为国内网络是下载不了C#/Xamain)的包的,还有各种各样的安装包,都已快接近奔溃的边缘.每次不 ...

  7. 自制MPLS解决路由黑洞实验

    利用mpls解决BGP路由黑洞配置命令全解析 --By Jim 什么是BGP路由黑洞? BGP规定无论路由器是否启动bgp都要无条件地转发BGP消息和更新包(凌驾于IGP之上),违背了IGP" ...

  8. 第二次作业:结对编程,四则运算的GUI实现

    小伙伴:201421123031 余洋 201421123044  潘志坚  题目要求: 我们在个人作业1中,用各种语言实现了一个命令行的四则运算小程序.进一步,本次要求把这个程序做成GUI(可以是W ...

  9. bean的生命周期以及延迟实例化

    可以指定bean的初始化创建的时候调用的方法,以及销毁的时候调用的方法. 通过指定中的init-method和destroy-method方法指定bean的创建和销毁的时候执行类中的方法. 把lazy ...

  10. 201521123001《Java程序设计》第7周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 参考资料: XMind 答: 大多数情况下,从性能上来说ArrayList最好,但是当集合内的元素需要频繁插入.删除时Lin ...