上次讲到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. MyEclipse 护眼配色

    因为长时间对着电脑,白底看着老累眼,所以想把myeclipse全部改成VS的黑主题,但最终无功而返.(读者有修改方案可以推荐给我哈,在此先谢谢了) 除了黑底的颜色主题,其实豆沙绿也是挺柔和的,下面是豆 ...

  2. ubuntu安装mysql可视化工具MySQL-workbench及简单操作

    一.使用命令行在ubuntu下安装mysql可视化工具MySQL-workbench Step1:安装MySQL-workbench 方案一:如果你已经装好mysql的相关服务,那么直接使用如下命令即 ...

  3. 2017年最好的JavaScript库

    iTour 它让你有机会告诉你任何接口的功能.此外,这个插件可以执行教学,显示任务的功能,提示你做什么和跟进任务的执行.这是他所有类似软件中最灵活和功能最强的插件. Popper.js 这是屏幕上的一 ...

  4. TensorFlow框架之Computational Graph详解

    1. Getting Start 1.1 import TensorFlow应用程序需要引入编程架包,才能访问TensorFlow的类.方法和符号.如下所示的方法: import tensorflow ...

  5. 7.21.01 if语句

    if语句 一个if语句包含一个布尔表达式和一条或多条语句. 语法 if语句的用语法如下: if(布尔表达式) { //如果布尔表达式为true将执行的语句 } 如果布尔表达式的值为true,则执行if ...

  6. vue2项目使用axios发送请求

    前言:在Vue1.0的时候有一个官方推荐的 ajax 插件 vue-resource,但是自从 Vue 更新到 2.0 之后,官方就不再更新 vue-resource. 目前主流的 Vue 项目,都选 ...

  7. Linux+.NetCore+Nginx搭建集群

    本篇和大家分享的是Linux+NetCore+Nginx搭建负载集群,对于netcore2.0发布后,我一直在看官网的文档并学习,关注有哪些新增的东西,我,一个从1.0到2.0的跟随者这里只总结一句话 ...

  8. Java温故而知新-空心菱形

    空心菱形 今天做题练手,题目是空心菱形,一开始没什么思路,去网上找了找,发现很难找到讲的通透的,自己现在独立做出来了,记录一下,以备后用. * * * * * * * * * * * * * * * ...

  9. MAC 上传文件到github

    在IOS中,经常需要上传文件到github.以桌面上的一个文件夹为例: 步骤1: cd 到该文件夹下,建立POD文件. $ cd /Users/andy/Desktop/openinstallSDK ...

  10. [2014-12-29]使用Enum位模式进行多重状态(或权限)管理

    前言 对于Enum在AspNet Mvc中的应用,我之前提到一种扩展,如何在 Asp.net Mvc 开发过程中更好的使用Enum.这里将介绍另一种更好的使用Enum的方法. Enum定义 以一个代表 ...