/**
* 对长数字字符串四舍五入
* @param string $num 长数字字符串
* @param int $places 保留小数位数
* @return string
*/
function sp_round(string $num, int $places): string
{
// 检验数字字符串
preg_match_all('/[^\d.]+/', $num, $matches);
if ($matches[0]) die('不合法的数字字符串');
if (!str_contains($num, '.'))
return $num;
$start = $end = ''; // 两个子字符串
$is_point = false; // 是否经过小数点
$point = 0; // 小数点后小数位数
$len = 0; // start字符串长度
// 拆分成两个子字符串
for ($i = 0; isset($num[$i]); ++ $i) {
if ($point < $places) {
$start .= $num[$i];
++ $len;
} else {
$end .= $num[$i];
}
if ($num[$i] == '.') $is_point = true;
if ($is_point) ++ $point;
if ($point == $places + 2) break;
}
// 四舍五入
$len2 = strlen($end);
if ($len2 == 2 && $end[1] > 4) {
if ($end[0] == 9) {
$end[0] = 0;
for ($i = $len - 1; $i > -1; -- $i) {
if ($start[$i] == '.') continue;
if ($start[$i] == 9) {
$start[$i] = 0;
} else {
$start[$i] = intval($start[$i]) + 1;
break;
}
}
if ($start[0] == 0) $start = '1' . $start;
} else {
$end[0] = intval($end[0]) + 1;
}
}
if ($len2) $end = $end[0];
return $start . $end;
}
v(sp_round('1234567897897465.123456', 3));
v(sp_round('1234567897897465.123456', 4));
v(sp_round('1234567897897465.109956', 4));
v(sp_round('9999999999999999.999956', 4));
执行结果: