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即使计算到60位后,依然还没有结束。在计算机中,32位的计算机中浮点数尾数部分是23位,64位的是52位。所以后面多出来的部分就会被舍弃掉。

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

$bin  "";

$int  = 7;

$base = 100;

echo "<table border=‘1‘>";

echo "<td width=‘50‘>位数</td>";

echo "<td width=‘50‘>x2</td>";

echo "<td width=‘50‘>位值</td>";

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

    echo "<tr>";

    echo "<td>$i</td>";

    $int $int * 2;

    echo "<td>$int</td>";

    if ($int == 100) {

        $bin.="1";

        echo "<td>1</td>";

        break;

    }

    if ($int > 100) {

        $bin.="1";

        $int $int $base;

        echo "<td>1</td>";

    else {

        $bin .= "0";

        echo "<td>0</td>";

    }

    echo "</td>";

    echo "</tr>";

}

echo "</table>";

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 "<br />";

echo number_format($f, 30);

时间: 2024-10-13 06:36:01

php关于金额比较引发的问题(转)的相关文章

BAT挺进移动竞争时代 红包引发入口之争

2016年新春佳节风潮涌动,BAT红包大战潮流来袭.今年的红包大战,似乎比以往来得更加激烈. 在腾讯与阿里分别用微信红包.QQ红包和支付宝红包点燃战火的时候,百度则通过它的技术优势另觅蹊径,用图像搜索.语音搜索技术的多模交互方式,构建了全新场景入口模式的红包玩法.其他互联网公司虽然在红包社交方面亦有创新和革新之处,但目前来看,整个局面仍以BAT引领的入口格局为甚.而这种全新形式下的场景入口方式,也或将在未来引领产业发展新局面. 差异化红包大战,技术成为新变量 春节期间,红包最能刺激用户数量和活跃

由爬虫引发的思考

前言 花了两天时间写一个简单的爬虫程序.目前所用的技术十分简单.就是获得目标页面的html文档内容,然后解析其中有用的内容.既没有实现模拟登陆,也没有任何防止反爬虫的措施,甚至没有使用多线程.不过在其中遇到的问题还是引发了我很多的思考与问题,比如爬虫的合法性问题以及爬虫的危害等.于是写下这篇文章记录一下.由于本人经验有限,引用参考了大量文章,有问题请指出. 爬虫的作用与危害 爬虫的作用 网络爬虫(Web Crawler),又称网络蜘蛛(Web Spider)或网络机器人(Web Robot),是

Vue引发的getter和setter

Vue引发的getter和setter 公司的新项目决定使用Vue.js来做,当我打印出Vue实例下的data对象里的属性时,发现了一个有趣的事情: 它的每个属性都有两个相对应的get和set方法,我觉的这是多此一举的,于是去网上查了查Vue双向绑定的实现原理,才发现它和Angular.js双向绑定的实现原理完全不同,Angular是用的数据脏检测,当Model发生变化,会检测所有视图是否绑定了相关数据,再更改视图.而Vue使用的发布订阅模式,是点对点的绑定数据. Vue的数据绑定只有两个步骤,

PHP实现数字金额转中文金额

 解决发票系统中,发票单上需要填写中文金额的问题:  function ToChineseNum($num) {         $zh_num = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];         $zh_unit = ['分', '角', '元', '拾', '佰', '仟', '万', '拾', '佰', '仟', '亿', '拾', '佰', '仟'];         if (!is_numeric(str_repl

25.购物金额结算

import java.util.Scanner; public class ShoppingList { /** * 购物金额结算 */ public static void main(String[] args) { double[] cashes = new double[5]; double sum = 0.0; //总金额 System.out.println("请输入会员本月的消费记录"); Scanner input = new Scanner(System.in); /

Android FastJson与不规范JSON引发的血案

去公司实习了,没多少时间更博客了,距离上一篇博客也有一个来月了.看标题,应该可以看出,这篇文章是讲一个坑,以及如何填坑. 坑是什么?有两个坑,其一是fastjson的bug,其二是不规范的json字符串.如何填坑,不要着急,后文详细说明. 首先,我们看一个json字符串 { "doubleParam": 4.875, "floatParam": 2.76, "extra": { "doubleParam": 12.23, &q

在C#中将金额转换成中文大写金额

具体代码如下: /// <summary> /// 金额转换成中文大写金额 /// </summary> /// <param name="LowerMoney">eg:10.74</param> /// <returns></returns> public static string MoneyToUpper(string LowerMoney) { string functionReturnValue = nu

mono 3.10 Socket引发未知异常的悲剧事情...

最近在做mono 3.10针对socket性能方面的测试,在想对压力比较高的情况下发现了mono的一个bug会导致程序引发未知异常从而导致程序结束的严重问题. 错误信息 Unhandled Exception: System.InvalidOperationException: EndSend can only be called once per asynchronous operation at System.Net.Sockets.Socket.EndSend (IAsyncResult

js jquery版本的 金额千分位转换函数(非正则,效率极高)

没想到js里面没有 金额千分位格式化的处理函数(例:1,234.01 这样的格式),网上搜了一圈,都是使用正则的方式处理的.正则的效率不敢恭维啊,又耗费资源速度又慢(虽然处理起来会直观一些). 因此专门写了一个纯数值处理最后输出字符串个 金额千分位处理函数,并封装成jQuery函数包,处理时效率很高,可高频率的使用,直接上代码.还有min压缩版本可点击连接下载. 如果你不是jQuery环境,直接把源码拿出来,重新封装到自己的函数中能够就能用. 源码以及min包下载地址:jQuery.format