大胡笔记 • 2026-04-28 • 阅读
别再被小数搞晕!PHP四舍五入的三种核心玩法,一篇讲透
哈喽,写代码的朋友们,今天咱们聊一个看似简单、但新手经常掉坑里的问题——PHP四舍五入。你是不是也遇到过这种情况:明明用了round函数,结果却不是你想要的;或者银行计算金额时,四舍五入怎么跟数学课上学的不一样?大胡笔记当年也被这些小数折磨过,后来才搞明白,PHP里四舍五入不止一种玩法。今天我把最常用的三种方法以及它们的坑全部整理出来,保证你看完就能用对。
先搞清楚round()的基本用法,别连参数都搞反了
PHP中最基础的四舍五入函数就是round()。很多人用它的时候只传一个参数,比如round(3.14159),结果得到3,但其实它可以传三个参数,只是后面两个有默认值罢了。
大胡笔记给你拆解一下:round(float $num, int $precision = 0, int $mode = PHP_ROUND_HALF_UP)。第一个参数是要处理的数字,第二个参数是保留几位小数(默认0),第三个参数是舍入模式(后面专门讲)。
举个例子:
round(3.14159, 2) 返回 3.14,保留两位小数,第三位是1所以舍掉。
round(3.145, 2) 返回 3.15,因为第三位是5,向前进一。
很多人容易弄错的是负数的情况。比如round(12345, -2),返回12300,意思是保留到百位(小数点前两位)。负数参数在对大数进行近似时很有用。
大胡笔记提醒你,round()返回的是float类型,如果你需要精确的字符串比较,最好先number_format()或者转成字符串。
银行家舍入法是什么?为什么和数学老师教的不一样
你可能会发现,用round(2.5)得到3,这符合“四舍五入”。但有些金融系统里2.5其实会舍成2。这不是bug,而是另一种舍入标准——银行家舍入法(也叫“四舍六入五留双”)。
PHP支持这种模式,就是在round()的第三个参数里指定。大胡笔记给你列出最常用的几种模式。
PHP_ROUND_HALF_UP:标准四舍五入,2.5变成3,-2.5变成-3。这是默认的。
PHP_ROUND_HALF_DOWN:五舍六入,2.5变成2,-2.5变成-2。适合一些需要偏向小数的场景。
PHP_ROUND_HALF_EVEN:银行家舍入法。2.5变成2(因为2是偶数),3.5变成4(因为3是奇数,进一位后变4)。这种模式能减少大量数据累加时的舍入误差,常被用于金融计算。
PHP_ROUND_HALF_ODD:与偶数相反,2.5变成3,3.5变成3(因为3是奇数,不进位?等等,需要验证)。
大胡笔记建议:如果你在写电商订单金额、汇率换算这类对精度要求高的功能,最好问问财务用哪种规则,然后选对应的模式。普通场景用默认的就够了。
ceil()和floor(),向上向下取整的兄弟俩
除了round(),还有两个常用的取整函数:ceil()和floor()。它们不是严格意义上的“四舍五入”,但经常配合着用。
ceil()是“向上取整”,无论小数部分是多少,都往大的整数方向进一。例如ceil(3.14)得到4,ceil(3.99)也是4,ceil(-3.14)得到-3(因为-3 > -3.14)。
floor()是“向下取整”,直接舍弃小数部分。floor(3.14)得3,floor(3.99)也得3,floor(-3.14)得-4。
大胡笔记在实际开发中常这样用:分页计算总页数时,总记录数除以每页条数后向上取整,比如ceil($total / $perPage)。而计算折扣后的整数金额时,可能会用floor($price * $discount)来确保不超过预算。
另外,intval()截断取整和floor()对正数是一样的,但对负数不一样,intval(-3.14)得-3,而floor(-3.14)得-4。这个细节容易忽略,大胡笔记提醒你注意。
number_format()不只是格式化,还能间接四舍五入
很多人知道number_format()是用来给数字加千位分隔符的,其实它也会进行四舍五入。它的函数原型是:number_format(float $num, int $decimals = 0, string $dec_point = ".", string $thousands_sep = ",")。
当你指定小数位数时,number_format()会自动对最后一位进行四舍五入。例如:
number_format(3.14159, 2) 返回 "3.14"(字符串形式),同时去掉了3后面的数字。
number_format(3.145, 2) 返回 "3.15"。
这个函数的优势是返回的是字符串,并且带千位分隔符,很适合直接展示给用户。但缺点是你不能再拿它做数学计算,因为逗号会干扰运算。大胡笔记习惯在最后输出时才用number_format(),计算过程中保持原始float或者用高精度函数。
高精度计算别用float,试试bcmath系列函数
如果你在做钱相关的计算,比如0.1 + 0.2,直接用PHP的float可能会得到一个像0.30000000000000004这样的结果。这不是PHP的错,而是计算机二进制浮点数表示法的通病。这时候四舍五入前就需要用高精度数学函数。
PHP提供了BCMath扩展(前提是你安装了)。常用的函数有:
bcadd($left, $right, $scale) 加法
bcsub($left, $right, $scale) 减法
bcmul($left, $right, $scale) 乘法
bcdiv($left, $right, $scale) 除法
bcround($number, $precision)(注意:PHP 8.4+ 才有这个函数,之前的版本需要自己实现)
如果你的PHP版本低于8.4,可以自己写一个精准四舍五入函数:
php
function bcround($number, $precision = 0) {
$multiplier = bcpow('10', (string)$precision);
$rounded = bcdiv(bcadd(bcmul($number, $multiplier), '0.5'), $multiplier, $precision);
return $rounded;
}
大胡笔记建议:涉及金额、税费、汇率转换时,一定用BCMath,然后用bcround()或者手动计算后转成整数分(乘以100),避免浮点数误差。
实战案例:购物车总价计算时如何正确四舍五入
假设你有一个购物车,商品价格分别是12.345元和6.789元,数量分别为2和1,税率10%。要求最终金额四舍五入保留两位小数。
错误的做法:
text
$total = (12.345 * 2 + 6.789) * 1.1;
echo round($total, 2);
因为浮点数累积误差,可能得到34.99而不是35.00。
正确的BCMath做法:
php
$sub1 = bcmul('12.345', '2', 3); // 24.690
$sub2 = '6.789';
$sum = bcadd($sub1, $sub2, 3); // 31.479
$tax = bcmul($sum, '0.1', 3); // 3.1479
$total = bcadd($sum, $tax, 3); // 34.6269
// 最后四舍五入到两位小数,可以用自定义函数
$final = bcround($total, 2); // 34.63
大胡笔记在实际项目中,会先把所有金额乘以100转成分(整数)计算,最后再除以100。比如12.345元转成1234.5分?不,转成分时必须先精确到分,所以最好是12.35元乘以100=1235分。总之,BCMath是更稳妥的方案。
容易翻车的三个细节,大胡笔记血泪总结
第一个细节,round()对科学计数法的处理。 比如round(2.5E-1, 0),结果是0还是0?其实它会先转成0.25再四舍五入,得到0。但某些旧版本可能有bug,建议避免。
第二个细节,浮点数直接比较。永远不要用if(round($a,2) == round($b,2))来比较两个金额是否相等,因为浮点数结果可能带微小误差。应该用abs(round($a,2)-round($b,2)) < 0.0001,或者用BCMath比较。
第三个细节,中文环境下number_format()的坑。 如果你直接输出number_format(1234.56, 2),默认小数点是点,千位分隔符是逗号。但中文习惯可能是“1,234.56”或者“1234.56”。如果要输出“1 234.56”,需要自己调整参数。另外,它返回的是字符串,不要拿去做加法。
今天这篇关于PHP四舍五入的文章,大胡笔记把round()的多种模式、ceil/floor、number_format的格式化取整、BCMath高精度计算以及实战案例都过了一遍。最后给你三个建议:普通数字用round()默认模式;需要展示给用户时用number_format();涉及金额一定用BCMath或者转成整数分。希望下次你写代码遇到小数舍入时,能迅速选对工具,不再被奇怪的结果搞晕。如果你还有其他PHP数值处理的疑问,欢迎在评论区告诉大胡笔记,咱们一起探讨。下期再见!
转载请注明出处!大胡笔记:www.10i.com.cn