电商或财务系统计算钱精度的问题

在财务系统中经常,国内最小是分,比如说一件东西0.58元,如果买一百件那应该是58元吧? 用php表示计算

<?php

var_dump(0.58*100) ;//58
?>

结果正确,但是在看一下,下面的结果,怎么就变成57了呢?

<?php
var_dump(intval(0.58*100));//57
?>

是php的bug吗?

那让我们再看一下,javascript,运行下面的代码,你会发现第一个输出怎么不是0.3?

console.log(0.1 + 0.2);   
console.log(0.1 + 0.2 == 0.3);

因此php 表示这个锅,我不背,搞懂这些就要了解浮点数在计算机中表示的方法.

浮点数, 以64位的长度(双精度)为例, 会采用1位符号位(E), 11指数位(Q), 52位尾数(M)表示(一共64位).

0.58的二进制表示基本上(52位)是: 0010100011110101110000101000111101011100001010001111, 如果只是通过这52位计算的话是  0.58 -> 0.57999999999999996,至于0.58 * 100的具体浮点数乘法, 我们不考虑那么细,我们就模糊的以心算来看… 0.58 * 100 = 57.999999999

  那你intval一下, 自然就是57了….

因此浮点数的计算和比较是有误差的.

在php中如何来解决这个问题呢?可以有两种方法

方法1:PHP 为任意精度数学计算提供了二进制计算器(Binary Calculator),它支持任意大小和精度的数字,以字符串(非浮点数)形式描述

bcadd — 加法
bccomp — 比较
bcdiv — 相除
bcmod — 求余数
bcmul — 乘法
bcpow — 次方
bcpowmod — 先次方然后求余数
bcscale — 给所有函数设置小数位精度
bcsqrt — 求平方根
bcsub — 减法

方法2: 如果需要转成整形转换成多少分钱取整,最后第三个100是换回成多少元.

还是上面的 就是  round(0.58*100)*100/100; 

那前面挖的坑,在javascript也有对应的函数,Math.round,可以通过Math.round(0.58*100),将小数转换成整数,最后再除100,转换成元计算.

猜你喜欢

转载自blog.csdn.net/robinhunan/article/details/84231668