本文没什么好说的,点进来看吧… …

最近工作中,在处理电商用户下单模块的时候,之前遗留系统保留的是三位小数。这里带来了一个问题:
如果用户的订单价格是:6.666元,那么在向支付宝或者微信发起支付时,第三方只会保留两位数,也就是用户实际付款:6.66元。

那么问题来了,支付成功第三方回调系统接口,在接口中的逻辑需要比对支付的金额,会发现 6.666≠6.66,然后后面的逻辑无法运行,处理失败。

这里引申出来的一个问题是:我们对浮点数位数保留时,该使用何种方式?

php中提供了很多种处理浮点数位数的方式。

  • number_format — 以千位分隔符方式格式化一个数字
  • round — 对浮点数进行四舍五入 (通过传入第三个参数,可以控制舍、入的方式)
  • sprintf — Return a formatted string
  • bcadd — 2个任意精度数字的加法计算

还有一种方式就是字符串截取,比如以前三位小数,现在只要两位,就把最后一位截去。

但是,这种方式千万别用,非常无耻,万一某个数据查询出来是:9999元,你把最后一位搞掉变成:999元,自己赔公司损失吧。

说完处理的方式,不得不提一下性能问题。这里由于 round 函数如果数字后面全是0的话。不能正确处理小数位数,这里就先不提.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php

$data = array_fill(0, 1, range(1, 100000, 1.1))[0];

$start = microtime(true);
foreach ($data as $num) {
number_format(10000, 2, '.', '');
}
$end = microtime(true);
$need = $end - $start;
print("number_format time: {$need}" . PHP_EOL);

$start = microtime(true);
foreach ($data as $num) {
bcadd($num, 0, 2);
}
$end = microtime(true);
$need = $end - $start;
print("bcadd time: {$need}" . PHP_EOL);

$start = microtime(true);
foreach ($data as $num) {
sprintf('.2%f', $num);
}
$end = microtime(true);
$need = $end - $start;
print("sprintf time: {$need}" . PHP_EOL);

上面的代码运行,会得到以下结果

1
2
3
number_format time: 0.086385011672974
bcadd time: 0.098035097122192
sprintf time: 0.069508075714111

所以通过对比,推荐使用 sprintf 来处理浮点数位数的问题。