Incorrect terminology usage for rounding modes of round()
| From: | Gina P. Banyard | Date: | Sun, 14 Apr 2024 14:33:10 +0000 |
| Subject: | Incorrect terminology usage for rounding modes of round() | ||
| Groups: | php.internals | ||
| Request: | Send a blank email to internals+get-123126@lists.php.net to get a copy of this message | ||
Hello internals,
While reviewing the PR for the "Adding bcround, bcfloor and bcceil to BCMath" RFC [1] with
the different rounding modes,
I was made aware of the unfortunate wrong terminology usage of PHP_ROUND_HALF_UP and
PHP_ROUND_HALF_DOWN.
Indeed, PHP_ROUND_HALF_UP is implemented as "rounding half away from zero" and not
"rounding half up" (which is also called rounding toward positive infinity).
The behaviour for positive numbers is the same, however for negative numbers the rounding *is*
different.
In the same vein, PHP_ROUND_HALD_DOWN is implemented as "rounding half toward zero" and
not "rounding half down" (/round half toward negative infinity).
Taking -1.5 as our number:
- Rounding half-up: -1
- Rounding half away from zero: -2
- Rounding half-down: -2
- Rounding half towards zero: -1
For a detailed explanation about rounding, the Wikipedia page is of great use. [2]
And the following rounding calculator on Calculator Soup is useful to check the differences and
behaviour of different rounding modes. [3]
It should be noted that PHP is in good company about being wrong about those two rounding modes,
Java, Python, and Ruby (and probably others) are also wrong in this regard.
Considering that PHP 8.4 is also adding new rounding modes via the "Add 4 new rounding modes to
round() function" RFC [4] attempting to solve this issue in this next version of PHP seems like
a good idea.
In my discussions with Saki about this issue, it seems that her and Tim have thought about creating
a new enum for rounding modes, looking something like this:
enum RoundingMode {
case HalfAwayFromZero;
case HalfTowardsZero;
case HalfEven;
case HalfOdd;
case TowardsZero;
case AwayFromZero;
case NegativeInfinity; // or case Floor;
case PositiveInfinity; // or case Ceiling;
}
and change the signature of round from:
round(int|float $num, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): float
to
round(int|float $num, int $precision = 0, RoundingMode $mode = RoundingMode::HalfAwayFromZero):
float
and changing the definition of the existing constants to effectively be:
define('PHP_ROUND_HALF_UP', RoundingMode::HalfAwayFromZero);
define('PHP_ROUND_HALF_DOWN', RoundingMode::HalfTowardsZero);
define('PHP_ROUND_HALF_EVEN', RoundingMode::HalfEven);
define('PHP_ROUND_HALF_ODD', RoundingMode::HalfOdd);
This should not cause any BC breaks, while allowing us to potentially implement the half up/down
rounding modes properly, and deprecate the existing rounding constants in the future to get rid of
the confusing names.
I wanted to know if anyone has any object to introducing this new enum and signature change.
The only thing I could think of is if this enum should be in a new Maths (or Math or just
Mathematics to not need to deal with regional difference in the short spelling of
"Mathematics") namespace.
Best regards,
Gina P. Banyard
[1] https://wiki.php.net/rfc/adding_bcround_bcfloor_bcceil_to_bcmath
[2] https://en.wikipedia.org/wiki/Rounding
[3] https://www.calculatorsoup.com/calculators/math/rounding-methods-calculator.php
[4] https://wiki.php.net/rfc/new_rounding_modes_to_round_function