rfc:integer-rounding

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:integer-rounding [2023/09/26 11:09] – link discussion & PR maberfc:integer-rounding [2024/04/02 07:07] (current) – Declined mabe
Line 1: Line 1:
 ====== PHP RFC: Rounding Integers as int ====== ====== PHP RFC: Rounding Integers as int ======
-  * Version: 0.1+  * Version: 0.2
   * Date: 2023-09-26   * Date: 2023-09-26
   * Author: Marc Bennewitz, php@mabe.berlin   * Author: Marc Bennewitz, php@mabe.berlin
-  * Status: Under Discussion+  * Status: Declined
   * First Published at: https://wiki.php.net/RFC/integer-rounding   * First Published at: https://wiki.php.net/RFC/integer-rounding
  
Line 13: Line 13:
 Except ''number_format'' all of these functions take a ''float|int'', do a cast to ''float'', process on floating point number only and finally return a '' float''. Except ''number_format'' all of these functions take a ''float|int'', do a cast to ''float'', process on floating point number only and finally return a '' float''.
  
-As a result of ''round'', ''ceil'' and ''floor'' you get a floating point number rounded to your needs.+As a result of ''round'', ''ceil'' and ''floor'' the resulting value will be a floating point number rounded to your needs.
  
-In most cases this is sufficient but in cases of handling with integer values above 2^53 you start to end up with unexpected results due to floating point arithmetic and precision loss.+In most cases this is sufficient but in cases of handling integer values above 2^53 you start to end up with unexpected results due to floating point arithmetic and precision loss.
  
-''number_format'' on the other hand produces human readable numbers as ''string'' and since PHP 8.3 it also performs rounding integers without casting it to floating point numbers to fix the above issues mentioned.+''number_format'' on the other hand produces human readable numbers as ''string'' and since PHP 8.3 it also performs rounding integers without casting it to floating point numbers to fix the above issue mentioned.
  
  
 ===== Proposal ===== ===== Proposal =====
  
-This RFC proposes to perform rounding on given integer values and also return the resulting integer if possible. +This RFC proposes to perform rounding on given int and return a rounded int value if possible by default
-In case of integer under-/overflow the value will be casted to a float and rounded based on the floating point number as it's done currently.+In case of integer under-/overflow the value will be cast to a float (double) and rounded based on the floating point number as it's done currently.
  
-For ''ceil'', ''floor'' and ''round'' with ''precision >= 0'' this means a given integer argument gets returned as is.+For ''ceil'', ''floor'' and ''round'' with ''precision >= 0'' this means a given int gets returned as is
 +---- 
 +Rounding a given float will perform rounding on floating point number directly and return float as it's done currently. 
 +There will be no implicit cast to int because floats (double) can represent a much wider range of numbers than int (32 or 64 bit).
  
-The ''int''-type is compatible to ''float'', returning an integer instead of a floating point number will still be a number accepted for type-hints of ''int''''float''''int|float'or ''mixed'' with strict_types enabled.+Floating point numbers on the other hand gets imprecise on representing numbers > 2^53 but passing a float to be rounded such imprecision must be known in first place already. 
 +---- 
 +An additional argument will be introduced ''bool force_float''
 +In PHP 8.next this will default to ''force_float=true'' to keep current behavior but the new behavior can already be used by passing ''force_float=false''
 +In PHP 9.0 the default will change to ''force_float=false'' to get the new behavior by default but the previous behavior can be forced with ''force_float=true''.
  
-Even if the returned type is compatible to the current returned type this is considered a breaking change on high frequently used functions and as such there will be a deprecation phase as described in "Backward Incompatible Changes" below. 
  
-As a result PHP will behave more precise on rounding integer values.+The behavior will be as follows: 
 +<code> 
 +// No change, rounding floating point number will keep processing and returning float as it does now 
 +ceil(float, force_float=false): float 
 +ceil(float, force_float=true): float 
 +floor(float, force_float=false): float 
 +floor(float, force_float=true): float 
 +round(float, force_float=false): float 
 +round(float, force_float=true): float 
 + 
 +// rounding integers with force_float=false will process and return int if possible 
 +ceil(int, force_float=false): int 
 +ceil(int, force_float=true): float 
 +floor(int, force_float=false): int 
 +floor(int, force_float=true): float 
 +round(int, precision: >= 0, force_float=false): int 
 +round(int, precision: < 0, force_float=false): int|float // implicit cast to float only in case of integer under-/overflow 
 +round(int, force_float=true): float 
 +</code> 
 + 
 +This will result in less implicit casts and more precise rounding of integers above 2^53. 
 + 
 +The ''int''-type is compatible to ''float'', returning an int instead of a float will still be a number accepted for type-hints of ''int'', ''float'', ''int|float'' or ''mixed'' with strict_types enabled. 
 + 
 +As a result PHP will behave more precisely on rounding integer values.
  
  
Line 42: Line 72:
 var_dump(round(1.0)); var_dump(round(1.0));
 var_dump(round(987654321098765432, precision: -3)); var_dump(round(987654321098765432, precision: -3));
-var_dump(round(987654321098765432, precision: -4));+var_dump(round(987654321098765432, precision: -4)); // integer overflow 
 +var_dump(round(10000000000000055296)); // input is float
 var_dump(takeFloat(round(987654321098765432, precision: -3))); var_dump(takeFloat(round(987654321098765432, precision: -3)));
 var_dump(returnFloat(round(987654321098765432, precision: -3))); var_dump(returnFloat(round(987654321098765432, precision: -3)));
 +
 +echo "\n#########################\n";
 +
 +var_dump(round(1, force_float=true));
 +var_dump(round(1.0, force_float=true));
 +var_dump(round(987654321098765432, precision: -3, force_float=true));
 +var_dump(round(987654321098765432, precision: -4, force_float=true)); // integer overflow
 +var_dump(round(10000000000000055296, force_float=true)); // input is float
 +var_dump(takeFloat(round(987654321098765432, precision: -3, force_float=true)));
 +var_dump(returnFloat(round(987654321098765432, precision: -3, force_float=true)));
 +
 +echo "\n#########################\n";
 +
 +var_dump(round(1, force_float=false));
 +var_dump(round(1.0, force_float=false));
 +var_dump(round(987654321098765432, precision: -3, force_float=false));
 +var_dump(round(987654321098765432, precision: -4, force_float=false)); // integer overflow
 +var_dump(round(10000000000000055296, force_float=false)); // input is float
 +var_dump(takeFloat(round(987654321098765432, precision: -3, force_float=false)));
 +var_dump(returnFloat(round(987654321098765432, precision: -3, force_float=false)));
 </code> </code>
  
Line 53: Line 104:
 float(9.87654321098766E+17) float(9.87654321098766E+17)
 float(9.8765432109877E+17) float(9.8765432109877E+17)
 +float(1.0000000000000055E+19)
 float(9.87654321098766E+17) float(9.87654321098766E+17)
 float(9.87654321098766E+17) float(9.87654321098766E+17)
 +#########################
 +Unknown named parameter $force_float
 </code> </code>
  
-8.4 behavior (deprecation message):+8.4 behavior (no change by default but possible to opt-in to new behavior):
 <code> <code>
-Rounding an integer will return an integer instead of a float in the future, consider casting argument 'num' to float to keep current behavior. 
 float(1) float(1)
 float(1) float(1)
-Rounding an integer will return an integer instead of a float in the future, consider casting argument 'num' to float to keep current behavior. 
 float(9.87654321098766E+17) float(9.87654321098766E+17)
 float(9.8765432109877E+17) float(9.8765432109877E+17)
-Rounding an integer will return an integer instead of a float in the future, consider casting argument 'num' to float to keep current behavior.+float(1.0000000000000055E+19) 
 +float(9.87654321098766E+17) 
 +float(9.87654321098766E+17) 
 +######################### 
 +float(1) 
 +float(1) 
 +float(9.87654321098766E+17) 
 +float(9.8765432109877E+17) 
 +float(1.0000000000000055E+19) 
 +float(9.87654321098766E+17) 
 +float(9.87654321098766E+17) 
 +######################### 
 +int(1) 
 +float(1) 
 +int(987654321098765000) 
 +float(9.8765432109877E+17) 
 +float(1.0000000000000055E+19)
 float(9.87654321098766E+17) float(9.87654321098766E+17)
-Rounding an integer will return an integer instead of a float in the future, consider casting argument 'num' to float to keep current behavior. 
 float(9.87654321098766E+17) float(9.87654321098766E+17)
 </code> </code>
  
-9.0 behavior (Changed behavior):+9.0 behavior (Changed default behavior):
 <code> <code>
 int(1) int(1)
Line 77: Line 144:
 int(987654321098765000) int(987654321098765000)
 float(9.8765432109877E+17) float(9.8765432109877E+17)
 +float(1.0000000000000055E+19)
 +float(9.87654321098766E+17)
 +float(9.87654321098766E+17)
 +#########################
 +float(1)
 +float(1)
 +float(9.87654321098766E+17)
 +float(9.8765432109877E+17)
 +float(1.0000000000000055E+19)
 +float(9.87654321098766E+17)
 +float(9.87654321098766E+17)
 +#########################
 +int(1)
 +float(1)
 +int(987654321098765000)
 +float(9.8765432109877E+17)
 +float(1.0000000000000055E+19)
 float(9.87654321098766E+17) float(9.87654321098766E+17)
 float(9.87654321098766E+17) float(9.87654321098766E+17)
Line 83: Line 167:
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
  
-A BC break happens on type reporting functions like ''is_float'', ''get_debug_type'', ''gettype'' and strict comparison ''==='' will now report an integer. Because of that there will be a deprecation phase so users are able to detect these cases and can act accordingly. +A BC break happens on type reporting functions like ''is_float'', ''get_debug_type'', ''gettype'' and strict comparison ''==='' will now report an int on rounding integers by default.
- +
-As such PHP 8.4 will report a deprecation message in case ''round'', ''ceil'' and ''floor'' would return an integer. For as long as the deprecation phase the resulting integer gets casted to ''float'' before return. +
- +
-The old implicit behavior can be forced by explicitly casting the value ''round((float)$num)''. This does not trigger a deprecation message and will not change behavior after the deprecation phase.+
  
-In PHP 9.0 the new behavior on rounding integers will be in place without further messages.+The old behavior can be forced by explicitly passing `force_float=true` argument or by explicitly casting the result back to float ''(float)round($num)''.
  
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
-  * Add deprecation message in PHP 8.next +  * Introduce new argument ''force_float=true'' in PHP 8.next 
-  * Change behavior as described in next major version.+  * Change default to ''force_float=false'' as described in PHP 9.
  
 ===== RFC Impact ===== ===== RFC Impact =====
Line 104: Line 184:
  
 ==== To Opcache ==== ==== To Opcache ====
-It is necessary to develop RFC's with opcache in mind, since opcache is a core extension distributed with PHP. +none
- +
-Please explain how you have verified your RFC's compatibility with opcache.+
  
 ==== New Constants ==== ==== New Constants ====
Line 118: Line 196:
  
 ===== Unaffected PHP Functionality ===== ===== Unaffected PHP Functionality =====
-Rounding floating point numbers will not be effected in any way.+Rounding floating point numbers will not be affected in any way.
  
 ===== Future Scope ===== ===== Future Scope =====
-This section details areas where the feature might be improved in future, but that are not currently proposed in this RFC.+After PHP 9 it can be considered to deprecate and remove the ''force_float'' argument again but due to very long future this is not part of this RFC.
  
-===== Proposed Voting Choices ===== + 
-Include these so readers know where you are heading and can discuss the proposed voting options.+===== Proposed Voting Choices =====  
 +As per the voting RFC a yes/no vote with a 2/3 majority is needed for this proposal to be accepted. 
 +Voting started on 2024-03-17 and will end on 2024-04-02 00:00 GMT. 
 +<doodle title="Rounding Integers as int" auth="Marc Bennewitz" voteType="single" closed="false" closeon="2024-04-02T00:00:00Z"> 
 +   * Yes 
 +   * No 
 +</doodle>
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
Line 147: Line 231:
 PHP is a loosely typed langue and as such it's extremely uncommon to have to call different rounding functions for rounding ''int'' vs. ''float''. PHP is a loosely typed langue and as such it's extremely uncommon to have to call different rounding functions for rounding ''int'' vs. ''float''.
 Also the normal ''round'' already does the trick and mostly you won't know better until you get bitten by floating point. Also the normal ''round'' already does the trick and mostly you won't know better until you get bitten by floating point.
- 
-=== Add function argument === 
- 
-Same as "Add new function" PHP is a loosely typed langue and as such it's extremely uncommon to have to define different arguments for ''int'' vs. ''float''. 
  
  
  
rfc/integer-rounding.1695726584.txt.gz · Last modified: 2023/09/26 11:09 by mabe