rfc:make_round_behave_correctly_as_float

PHP RFC: Correct the behavior of round() to follow proper floating-point semantics.

  • Version: 0.9
  • Date: 2025-06-25
  • Author: Saki Takamachi, saki@php.net
  • Status: Draft
  • Implementation: yet

Introduction

In 2023, PHP RFC: Change the edge case of round() was rejected. That proposal aimed to invalidate the definition set by https://wiki.php.net/rfc/rounding , and to correct the behavior of round() to follow proper floating-point semantics.

<?php
 
/* Since 0.285 is actually approximated as 0.28499999999999998,
 * rounding it to 0.28 is the correct behavior from a floating-point perspective. */
round(0.285, 2); // 0.29
 
?>

However, among the votes against the previous proposal, some expressed support for the overall direction but opposed targeting a minor version like 8.4 due to the scale of the change.

Additionally, at the time of the previous vote, there was no alternative available for users who preferred the old behavior. With the introduction of bcround() in BCMath in PHP 8.4, we are now in a position to offer a suitable alternative.

Proposal

This RFC, like the previous one, proposes modifying the round() function to follow correct floating-point behavior.

“Correct floating-point behavior” means that a floating-point value is always treated as a single, consistent approximation. Even if the result appears strange when viewed as a decimal, the behavior should reflect the inherent imprecision of floating-point arithmetic as it is, without attempting to correct it for visual consistency.

In most cases, the result remains unchanged, but in certain cases, the outcome may differ.

<?php
 
round(0.285, 2); // 0.29 => 0.28
round(1.555, 2); // 1.56 => 1.55
round(0.285, 3, RoundingMode::TowardsZero); // 0.285 => 0.284
 
?>

The reason for such outcomes is that, for example, the correct floating-point approximation of 0.285 is actually 0.28499999999999998. As a result, it may not meet the edge case threshold for RoundingMode::HalfAwayFromZero, or may end up being off by 1 when rounded RoundingMode::TowardsZero.

Issues with the current implementation

In the current implementation, values that should not be considered edge cases in floating-point arithmetic are sometimes treated as such, leading to the following issues.

<?php
 
var_dump(8.000000000000000 === 8.0000000000000005); // true
 
round(8.0, 15, RoundingMode::HalfAwayFromZero); // 8.000000000000002
round(8.0, 15, RoundingMode::AwayFromZero);     // 8
round(8.0, 15, RoundingMode::PositiveInfinity); // 8
 
?>

This occurs in cases where, at extremely high precision, values ending in 0 and those ending in 5 become indistinguishable due to floating-point error.

Such inconsistencies arise when decimal-like behavior is forcibly applied to floating-point numbers. Floating-point values inherently represent a range of possible values due to precision limitations, and to handle them correctly, a single consistent approximation should be used within that range at all times.

These contradictions primarily stem from how edge cases are currently handled under RoundingMode::HalfAwayFromZero, RoundingMode::AwayFromZero, and RoundingMode::PositiveInfinity. The implementation prioritizes decimal-like visual behavior, selectively interpreting “convenient” parts of the floating-point range for each case. As a result, the interpretation of the same floating-point value can vary depending on the rounding mode, leading to inconsistent behavior.

Backward Incompatible Changes

The behavior of round() function near edge cases will be changed. Additionally, number_format(), which internally relies on this functionality, will also be affected.

Proposed PHP Version(s)

PHP 9.0

RFC Impact

To the Ecosystem

This change will affect userland code by altering the behavior of round() and number_format() in certain edge cases. However, it only applies to a very specific subset of those edge cases.

To Existing Extensions

ext/standard

To SAPIs

None.

Open Issues

None.

Future Scope

None.

Voting Choices

Please consult the php/policies repository for the current voting guidelines.

Correct the behavior of round() to follow proper floating-point semantics.
Real name Yes No
Final result: 0 0
This poll has been closed.

Patches and Tests

yet.

Implementation

yet.

References

Rejected Features

None..

Changelog

If there are major changes to the initial proposal, please include a short summary with a date or a link to the mailing list announcement here, as not everyone has access to the wikis' version history.

rfc/make_round_behave_correctly_as_float.txt · Last modified: by saki