0% прочитано

I accidentally ran a PHP 8.3 project on PHP 8.4 and my tests caught a difference in round()

A small observation about the round() behavior difference between PHP 8.3 and PHP 8.4 that I discovered while working on two different projects.

20 липня 2026 р. 2 хв читанняPHP

An Unexpected Test Failure

I was working on two projects in parallel: one used PHP 8.3, while the other used 8.4. PHP 8.4 was active on my system, and I accidentally ran the older project’s tests without switching versions.

That kind of version-switching mistake later pushed me to set up a Laradock workflow for multiple PHP versions, where the correct PHP version is selected automatically for each project.

До
PHP 8.3 test execution result
Після
PHP 8.4 test execution result

The screenshots show the result of running the exact same test on PHP 8.3 and PHP 8.4. No code changes were made, only the PHP version was different.

A minimal test that reproduces the difference
<?php declare(strict_types=1); namespace Tests\Unit; use PHPUnit\Framework\TestCase; final class RoundBehaviorTest extends TestCase{    public function testRoundBehaviorBetweenPhpVersions(): void    {        $totalCommission = 123.456 + 78.999;        $commissionWithoutVat = round(168.7125, 2);         $commissionVat = round(            $totalCommission - $commissionWithoutVat,            2,        );         $this->assertSame(33.75, $commissionVat);    }}

What Changed in PHP 8.4

The most interesting part was that both PHP versions passed the same intermediate value to round(): 33.74499999999997613.

On PHP 8.3, it was rounded to 33.75, while PHP 8.4 returned 33.74. That small difference was enough to make a previously passing test fail.

PHP 8.4 changed how round() handles values located near a rounding boundary. Previously, the function tried to behave as if a float were an exact decimal value and could treat numbers extremely close to the boundary as the next halfway value.

Starting with PHP 8.4, round() uses the actual binary floating-point value. In this example, the function does not receive an exact 33.745, but:

33.74499999999997613

This value is slightly lower than 33.745, so rounding it to two decimal places produces 33.74.

PHP 8.3 compensated for this floating-point difference and returned 33.75, while PHP 8.4 no longer treats the value as an exact halfway case. That change in edge-case handling is what caused the test to fail.

PHP 8.4 also introduced the RoundingMode enum and four additional rounding modes. However, those new modes are not the cause of this particular result—the difference comes from the updated handling of the underlying floating-point value.

This case was also a good reminder that when upgrading a Laravel project to a newer PHP version, it is not enough to confirm that the dependencies install successfully. I also need to run the real test suite and check whether the runtime itself behaves differently.

I wasn't looking for differences between PHP 8.3 and PHP 8.4. I was simply working on a project. That's exactly why I appreciate automated tests. Sometimes they reveal changes long before a developer even suspects something has changed.