PHP浮点数用法与问题总结

作者:袖梨 2022-06-24

一,php浮点数用法

PHP浮点类型取整之ceil — 进一法取整
说明
float ceil ( float value )
返回不小于 value 的下一个整数,value 如果有小数部分则进一位。ceil() 返回的类型仍然是 float,因为 float 值的范围通常比 integer 要大。

例子 1. ceil() 例子

 代码如下 复制代码

< ?php
echo ceil(4.3); // 5
echo ceil(9.999); // 10
?>

PHP浮点类型取整之floor — 舍去法取整

说明
float floor ( float value )
返回不大于 value 的下一个整数,将 value 的小数部分舍去取整。floor() 返回的类型仍然是 float,因为 float 值的范围通常比 integer 要大。

例子 1. floor() 例子

 代码如下 复制代码

< ?php
echo floor(4.3); // 4
echo floor(9.999); // 9
?>

PHP浮点类型取整之round — 对浮点数进行四舍五入
说明
float round ( float val [, int precision] )
返回将 val 根据指定精度 precision(十进制小数点后数字的数目)进行四舍五入的结果。precision 也可以是负数或零(默认值)。

例子 1. round() 例子

 代码如下 复制代码


< ?php
echo round(3.4); // 3
echo round(3.5); // 4
echo round(3.6); // 4
echo round(3.6, 0); // 4
echo round(1.95583, 2); // 1.96
echo round(1241757, -3); // 1242000
echo round(5.045, 2); // 5.05
echo round(5.055, 2); // 5.06
?>

PHP浮点类型取整之intval—对变数转成整数型态
例子intval()

 代码如下 复制代码
< ?php
echo intval(4.3); //4
echo intval(4.6); // 4
?>

二,浮点数转成字符串

PHP内置的echo, var_dump, json_encode, 字符串拼接等函数(指令)在显示浮点数时都有问题, 导致精度丢失.

 代码如下 复制代码

  

   $a = 1315537636.338467;

   printf("%f", $a); echo "n";

   echo $a . "n";

   echo $a; echo "n";

  ?>

结果

  1315537636.338467

  1315537636.3385

  1315537636.3385

也就是说, 用PHP最顺手的方法将浮点数转成字符串或者显示是不行的, 必须使用printf/sprintf将浮点数转成字符串.


三,一个常见问题的解答

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

 代码如下 复制代码

    $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了….

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

四,float(浮点数)比较的问题

最近在开发一个合同管理系统的时候,涉及到两个浮点数比较,算是把我郁闷惨了。
在N久以前,就不晓得从哪里听来的一个“不要用等号去比较浮点数”的“真理”,自己平时也在用,好像没有出现啥问题,可这次问题总算是来了。

 代码如下 复制代码

$sum = "12300.00";
$a   = "10000.30";
$b   =  "2000.30";
$c   =   "299.40";
 
$sum = (float) $sum;
$s = (float) ($a+$b+$c);
var_dump($sum, $s);
var_dump($sum==$s);

结果是:

 代码如下 复制代码

float(12300)
float(12300)
bool(false)

后来才知道在PHP中,要比较两个浮点数的大小,可以用bccomp(参数1,参数2,小数位)来比较。

 代码如下 复制代码

$sum = "12300.00";
$a   = "10000.30";
$b   =  "2000.30";
$c   =   "299.40";
 
$sum = (float) $sum;
$s = (float) ($a+$b+$c);
var_dump($sum, $s);
var_dump(bccomp($sum,$s,2));

结果:

float(12300)
float(12300)
int(0) // 0表示两个浮点数值相等

bccomp函数具体用法参与PHP手册

相关文章

精彩推荐