php金额单位,php关于金额比较引发的问题

做电子商务的时候一般会涉及到金额的比较,按正常的思路来看用><=这些个符号就可以了。可是要是到程序上来搞这个的话就出大事了。现在看下这段代码:

$f = 0.07;

var_dump($f * 100 == 7);//输出false

输出结果会出乎大家意料,输出false,为什么会这样呢?其实这个和电脑中存储小数的原理有关。大家都知道计算机只能存储0和1,我们日常生活习惯使用的是10进制的数据,像0.07这个小数在计算机中存储时会有精度损失,以至于计算出来的结果会有偏差。

那么怎么解决这个问题?虽然计算机存储小数有偏差,但是偏差还是非常小,像上例中0.07 * 100如果显示出小数点后面20位的话,最终的值如下:

$f = 0.07;

//输出7.00000000000000088818

echo number_format($f * 100, 20);

可以看到已经在小数点10多位之后了。在实际中我们通常也不需要精确到后面这么多位数字,在金额方面通常精确到后面3位就好了。如果精确到小数点后面三位的话,0.07*100和7就会相等了。在php中提供了一个bccomp函数用来处理这方面的比较。

$f = 0.07;

var_dump($f * 100 == 7);

//输出0,表示两个数字精度为小数点后3位的时候相等

var_dump(bccomp($f * 100, 7, 3));

虽然最终解决了问题,但是还是想搞明白为什么0.07这样的浮点数会有精度损失,经过一段时间的研究,发现产生误差的原因:就在于浮点数的小数位在转换成二进制的时候产生的。

浮点数小数部分转换成二进制规则:乘2取整法,即每一步将十进制小数部分乘以2,所得积的小数点左边的数字(0或1)作为二进制表示法中的数字,直到满足精确度为止。

0.07这个小数的转换成二进制过程如下:

f9ab426da27561bb45f0d9065071bec1.png

浮点数小数部分转二进制

经过计算发现0.07即使计算到60位后,依然还没有结束。在计算机中,32位的计算机中浮点数尾数部分是23位,64位的是52位。所以后面多出来的部分就会被舍弃掉。

附:

测试都是在64位机器上进行的。

转换小数部分为二进制的代码:

$bin = "";

$int = 7;

$base = 100;

echo "

echo "

位数";

echo "

x2";

echo "

位值";

for ($i = 0; $i <= 60; $i++) {

echo "

";

echo "

$i";

$int = $int * 2;

echo "

$int";

if ($int == 100) {

$bin.="1";

echo "

1";

break;

}

if ($int > 100) {

$bin.="1";

$int = $int - $base;

echo "

1";

} else {

$bin .= "0";

echo "

0";

}

echo "";

echo "

";

}

echo "

";

echo $bin;

对上例转换的二进制进行反推:

/*

输出内容

0.070000000000000006661338147751

0.070000000000000006661338147751

*/

$f = 0.0;

$bin = "0001000111101011100001010001111010111000010100011110101110000";

$l = strlen($bin);

for ($i = 0; $i < $l; $i++) {

if ($bin[$i] > 0) {

$f = $f + pow(2, -($i + 1));

}

}

echo number_format($f, 30);

$f = 0.07;

echo "
";

echo number_format($f, 30);

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值