PHP读取表格都是精度,php 小数精度问题

项目中保留两位小数四舍五入遇到精度问题:

$num = 0.99;

$num1 = round($num, 2);//0.98999999999999999

$num2 = floatval($num);//0.98999999999999999

目前解决方案:

sprintf("%.2f", round($money, 2));//会自动四舍五入

echo substr(sprintf("%.3f",$n), 0, -1);//不四舍五入

测试结果:

var_dump(json_encode(round(0.99 ,2)));//0.98999999999999999

var_dump(round(0.99 ,2));//0.99

$f = 0.58;

var_dump(intval($f * 100));//57

关于等于57这个问题,我们可以分析一下:

浮点数的表示(IEEE 754:IEEE二进位浮点数算术标准):

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

符号位:最高位表示数据的正负,0表示正数,1表示负数。

指数位:表示数据以2为底的幂,指数采用偏移码表示

尾数:表示数据小数点后的有效数字.

0.58 对于二进制表示来说, 是无限长的值:

0.58的二进制表示基本上(52位)是: 0010100011110101110000101000111101011100001010001111

0.57的二进制表示基本上(52位)是: 001000111101011100001010001111010111000010100011110

而两者的二进制, 如果只是通过这52位计算的话,分别是:

0.58 –> 0.57999999999999996

0.57 –> 0.5699999999999999

所以,0.58 * 100 结果会:57.999999999,转成整型:57

关于浮点数的二进制表示可以参考:浮点数的二进制表示

类似:

(0.1 + 0.7) == 0.8//false

floor((0.1+0.7)*10)//7

//内部结果可能是:7.9999999999

//所以:不可能精确的用有限位数表达某些十进制分数

1/3=3.33333333333

//而3.333333333333333*3,却不是1

所以结论:

所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度数学函数或者 gmp 函数

建议使用高精度函数:

bcadd — 2个任意精度数字的加法计算

bccomp — 比较两个任意精度的数字

bcdiv — 2个任意精度的数字除法计算

bcmod — 对一个任意精度数字取模

bcmul — 2个任意精度数字乘法计算

bcpow — 任意精度数字的乘方

bcpowmod — Raise an arbitrary precision number to another, reduced by a specified modulus

bcscale — 设置所有bc数学函数的默认小数点保留位数

bcsqrt — 任意精度数字的二次方根

bcsub — 2个任意精度数字的减法

使用高精度函数实现四舍五入:

function getBcRound($number, $precision = 0)

{

$precision = ($precision 

? 0

: (int) $precision;

if (strcmp(bcadd($number, '0', $precision), bcadd($number, '0', $precision+1)) == 0) {

return bcadd($number, '0', $precision);

}

if (getBcPresion($number) - $precision > 1) {

$number = getBcRound($number, $precision + 1);

}

$t = '0.' . str_repeat('0', $precision) . '5';

return $number 

? bcsub($number, $t, $precision)

: bcadd($number, $t, $precision);

}

function getBcPresion($number) {

$dotPosition = strpos($number, '.');

if ($dotPosition === false) {

return 0;

}

return strlen($number) - strpos($number, '.') - 1;

}

$money = getBcRound(0.99, 2);

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值