场景
电商项目中, 有一场景: 对商品打折优惠过后, 可能会有小数产生, 这时候是选择 四舍五入? 还是 向上精确? 向下精确? 产品决定: 为了追求, 商家最大利益, 采取的是 用户实际付款, 向上精确!
比如: 用户购买一个耳机 售价: 100.5 元, 商家活动 九五折 那么, 实际付款: 100.5 * 0.95 = 95.475 元
这时, 最大单位为分, 需对 实际付款, 向上精确2位小数, 预期结果如下: 95.470 => 95.47 95.471 => 95.48 95.475 => 95.48 95.477 => 95.48 95.4701 => 95.48
向上精确2位
<?php
// 向上精确2位
function ceil_dec($var) {
$var = $var * 100;
return ceil($var) / 100;
}
echo ceil_dec(95.470);echo '<br>'; // 95.47
echo ceil_dec(95.471);echo '<br>';// 95.48
echo ceil_dec(95.475);echo '<br>';// 95.48
echo ceil_dec(95.477);echo '<br>';// 95.48
echo ceil_dec(95.4701);echo '<br>';// 95.48
echo '<hr>';
当然, 肯定也可以, 向下精确2位
// 向下精确2位
function floor_dec($var) {
$var = $var * 100;
return floor($var) / 100;
}
对方法, 优化: 可以精确到指定小数位
<?php
// 向上精确指定位
function ceil_dec($var, $ext = 2) {
$pow = pow(10, $ext);
$var = $var * $pow;
return ceil($var) / $pow;
}
// 向下精确指定位
function floor_dec($var, $ext = 2) {
$pow = pow(10, $ext);
$var = $var * $pow;
return floor($var) / $pow;
}
但是, 还是有BUG! 偶然, 测试 0.07 向上取2位小数, 却得0.08, 理论应该是0.07
<?php
// 向上精确指定位
function ceil_dec($var, $ext = 2) {
$pow = pow(10, $ext);
$var = $var * $pow;
return ceil($var) / $pow;
}
echo ceil_dec(0.07, 2); // 输出: 0.08, 理论应该是0.07
排查了一番, 发现问题是小数运算精度丢失导致的:
,
,
,
$var = $var * $pow; // 7 = 0.07 * 100, 注意这里已经发生小数精度丢失
ceil($var); // 8 = ceil(7), 这里ceil去整就会发生异常, 明明 ceil(7) 等于7 , 却等于8
,
,
,
测试代码:
var_dump(ceil(0.07 * 100)); // float(8)
var_dump(ceil(7)); // float(7)
使用 PHP 高精度运算函数, 修复此BUG
<?php
// 向上精确指定位
function ceil_dec($var, $ext = 2) {
$pow = pow(10, $ext);
// $var = $var * $pow;
$var = bcmul($var, $pow, 3);
return ceil($var) / $pow;
}
echo ceil_dec(0.07, 2); // 0.07
// 向下精确指定位
function floor_dec($var, $ext = 2) {
$pow = pow(10, $ext);
// $var = $var * $pow;
$var = bcmul($var, $pow, 3);
return floor($var) / $pow;
}