round($num, $precision, $mode) rounds to $precision decimal places (negative values round to tens or hundreds); floor() and ceil() return whole-number floats. Since PHP 8.4 $mode accepts the RoundingMode enum, which renames the PHP_ROUND_* constants (HALF_UP is now HalfAwayFromZero, the default) and adds four directed modes.
<?php
$values = [2.5, 3.5, -2.5, 2.4, -2.6];
printf("%-17s%s\n", 'mode', implode('', array_map(fn($v) => sprintf('%6s', $v), $values)));
foreach (RoundingMode::cases() as $mode) {
printf("%-17s%s\n", $mode->name,
implode('', array_map(fn($v) => sprintf('%6s', round($v, 0, $mode)), $values)));
}
echo round(1234.5678, 2), ' ', round(1234.5678, -2), ' ', round(0.285, 2), ' ',
sprintf('%.2f', 0.285), ' ', json_encode([floor(-2.5), ceil(-2.5)]), "\n";mode 2.5 3.5 -2.5 2.4 -2.6 HalfAwayFromZero 3 4 -3 2 -3 HalfTowardsZero 2 3 -2 2 -3 HalfEven 2 4 -2 2 -3 HalfOdd 3 3 -3 2 -3 TowardsZero 2 3 -2 2 -2 AwayFromZero 3 4 -3 3 -3 NegativeInfinity 2 3 -3 2 -3 PositiveInfinity 3 4 -2 3 -2 1234.57 1200 0.29 0.28 [-3,-2]
HalfEven (banker's rounding) keeps long columns of totals from drifting. The directed modes work at any precision: round($fee, 2, RoundingMode::PositiveInfinity) is a ceiling to the cent. round() rounds the decimal you wrote (0.29), while sprintf('%.2f') rounds the stored 0.28499999... (0.28); PHP 8.4 rewrote this code and fixed an old "pre-rounding" bug. number_format() rounds like round() (printf and Formatting). Round once, at display or storage.