Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-rounding
Best,
Marc
Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-roundingBest,
Marc
I'm honestly kind of confused by this. The basic concept, as I understand it, is sound. But the rest of the description seems to suggest that the returned type will depend on the passed-in type? That makes little sense to me. Since we already know that int is contravariant to float, I would expect round to return int if it can, float if there is a decimal precision set. ceil and floor should always return ints, unconditionally, because that's literally what their purpose is. But that RFC doesn't say that, which makes me quite confused.
Similarly, the BC shim would therefore be (float)ceil($x), not ceil((float)$x), which... I don't think that would even do anything, would it?
--Larry Garfield
Hi Larry,
Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-roundingBest,
Marc
I'm honestly kind of confused by this. The basic concept, as I understand it, is sound. But the rest of the description seems to suggest that the returned type will depend on the passed-in type? That makes little sense to me. Since we already know that int is contravariant to float, I would expect round to return int if it can, float if there is a decimal precision set. ceil and floor should always return ints, unconditionally, because that's literally what their purpose is. But that RFC doesn't say that, which makes me quite confused.Similarly, the BC shim would therefore be (float)ceil($x), not ceil((float)$x), which... I don't think that would even do anything, would it?
Currently, all three functions return float, no matter what. This is an
implicit cast and results in inaccurate values for numbers above 2^53.
The RFC proposes to keep the given type if possible.
Given that, the return value is (and was) guaranteed to be float if the
given number is float.
If the given number is int the return value will most likely be int
except in case of integer over- / underflow.
If instead all integers would be returned as int-type, we would have
additional issues...
-
additional BC due to additional implicit cast from float to int
-
less accuracy on 32bit platforms (numbers > 2^31)
-
what to do on integer overflow?
--Larry Garfield
Marc
Hi, Marc.
There is one additional case to consider.
It's backwards compatible.
If strict_types
is not 1
, the code currently out there may be passing int
to these functions.
Therefore, it is better to be aware that such code can have destructive effects, and to consider whether or not to allow it.
Regards.
Saki
Hi Saki,
Hi, Marc.
There is one additional case to consider.
It's backwards compatible.
Ifstrict_types
is not1
, the code currently out there may be passingint
to these functions.Therefore, it is better to be aware that such code can have destructive effects, and to consider whether or not to allow it.
I'm not sure I understand.
All of these functions already take an int|float
and therefore the
setting of strict_types
is irrelevant here on passing float vs. int.
Regards.
Saki
Hi, Marc.
All of these functions already take an
int|float
and therefore the setting ofstrict_types
is irrelevant here on passing float vs. int.
Oops, excuse me, I completely misunderstood the signature.
Saki
ceil and floor should
always return ints, unconditionally, because that's literally what
their purpose is. But that RFC doesn't say that, which makes me quite
confused.
They can't do that. ceil(2.4e102) can't be repesented as an integer.
Similarly, the BC shim would therefore be (float)ceil($x), not
ceil((float)$x), which... I don't think that would even do anything,
would it?
(float) $int could also do something:
$ php -dprecision=20 -r 'echo (float) 1152921504606846974;'
1152921504606846976
$ php -dprecision=20 -r 'var_dump(ceil((float) 1152921504606846974));'
double(1152921504606846976)
(see how the 74 changes into 76 at the end).
Integers in the ranges LONG_MAX to 2^53, and -2^53 to LONG_MIN can't be
represented as whole numbers as float, and some precision is lost.
cheers,
Derick
Hi
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-rounding
I find the proposal reasonable, but I don't like the deprecation
proposal, because it does not allow me to opt into the new behavior
before PHP 9.0 arrives.
Getting rid of the deprecation would also require me to either add a
cast everywhere or add the error suppression operator everywhere and
then revert that with PHP 9 depending on whether I want the new behavior
or not.
As far as I can tell all the other deprecations allow me to use the
replacement functionality right away without needing to go through a
two-step process.
As the results of not casting to float first are arguably more correct,
perhaps this could be considered a bugfix. While it would cause breakage
for users that rely on strict types, obtaining the old behavior is just
one (float)
cast away.
Best regards
Tim Düsterhus
Hi Tim,
Hi
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-roundingI find the proposal reasonable, but I don't like the deprecation
proposal, because it does not allow me to opt into the new behavior
before PHP 9.0 arrives.Getting rid of the deprecation would also require me to either add a
cast everywhere or add the error suppression operator everywhere and
then revert that with PHP 9 depending on whether I want the new
behavior or not.As far as I can tell all the other deprecations allow me to use the
replacement functionality right away without needing to go through a
two-step process.As the results of not casting to float first are arguably more
correct, perhaps this could be considered a bugfix. While it would
cause breakage for users that rely on strict types, obtaining the old
behavior is just one(float)
cast away.
The deprecation would act as an information for upcoming behavior
change, not classical deprecation of future removal. If the new behavior
is what you want, than you don't need to change anything as a
deprecation message should not break anything. At the same time people
relying on the old behavior of implicit cast gets informed that they
have to cast explicit now.
I first was playing around with adding a new argument. First by default
keep existing behavior, in next changing default to new behavior and
later again get rid of the argument. But this would force users to
migrate multiple times and it would take years to finish.
Changing the behavior without further notice (deprecation) this could be
a huge BC ending up in a lot of uncaught bugs especially on such high
frequently used functions.
Best regards
Tim Düsterhus
Best,
Marc
Hi
The deprecation would act as an information for upcoming behavior
change, not classical deprecation of future removal. If the new behavior
is what you want, than you don't need to change anything as a
deprecation message should not break anything. At the same time people
Correct, the deprecation message itself would not break anything.
However when the new behavior is the behavior I desire, it would:
- Cause noise in my logs, because the deprecation would continue to be
emitted until the behavioral change is finally implemented. - Be confusing when working in preparing my application for future PHP
versions by handling all deprecations.
The only solutions would be either adding an '@' in front of every call
to round, ceil, floor (which might suppress additional errors I do
care about) or ignoring this specific deprecation within the error
handler of the application after verifying that I checked all locations
for correctness. There is no way for me to reliably mark only this
specific deprecation as acknowledged / handled for a specific place
within my code.
This also extends to library developers who are already under pressure
by users to make their libraries deprecation free on or before the
release of a new PHP version. As the library doesn't control the error
handler, they can't just ignore this specific deprecation for their
library, without casting the input to float, despite being happy with
the new behavior that will arrive in PHP 9.
In other words, this deprecation is not really actionable to me as a
developer, because I can't migrate to the new behavior on my own pace.
Best regards
Tim Düsterhus
Hi,
Hi
The deprecation would act as an information for upcoming behavior
change, not classical deprecation of future removal. If the new behavior
is what you want, than you don't need to change anything as a
deprecation message should not break anything. At the same time peopleCorrect, the deprecation message itself would not break anything.
However when the new behavior is the behavior I desire, it would:
- Cause noise in my logs, because the deprecation would continue to
be emitted until the behavioral change is finally implemented.- Be confusing when working in preparing my application for future
PHP versions by handling all deprecations.The only solutions would be either adding an '@' in front of every
call to round, ceil, floor (which might suppress additional errors I
do care about) or ignoring this specific deprecation within the
error handler of the application after verifying that I checked all
locations for correctness. There is no way for me to reliably mark
only this specific deprecation as acknowledged / handled for a
specific place within my code.This also extends to library developers who are already under pressure
by users to make their libraries deprecation free on or before the
release of a new PHP version. As the library doesn't control the error
handler, they can't just ignore this specific deprecation for their
library, without casting the input to float, despite being happy with
the new behavior that will arrive in PHP 9.In other words, this deprecation is not really actionable to me as a
developer, because I can't migrate to the new behavior on my own pace.
I do understand and I agree that it's not optimal but the issue is the
lack of good alternatives.
I only see three possible ways and all of them are suboptimal:
- Don't trigger upcoming behavior change message (deprecation message)
- impossible to opt-in new behavior in 8.4
- This could lead to different application behavior overlook on upgrade
to 9.0
- trigger upcoming behavior change message without possibility to
opt-in new behavior (as proposed)
- impossible to opt-in new behavior in 8.4
- either force old behavior or noise in logs
- introduce new argument
- possibility to opt-in new behavior in 8.4
- introducing a new argument which is already deprecated is just weird
- forcing users to migrate twice (set argument in 8.4 and remove in 9.0
or 10.0) - very long migration phase in case of removing argument in 10.0
I think it would be great in this case to have a way of triggering an
upcoming behavior change message even lower prio than a deprecation
message but I don't see this being possible.
As far as I understand you are in favor of option 1. (considering it a
bugfix) but how do you make sure this will not lead to different
application behavior overlooked on upgrading? Also there is no way to
opt-in new behavior.
Best regards
Tim Düsterhus
Best,
Marc
Hi
As far as I understand you are in favor of option 1. (considering it a
bugfix) but how do you make sure this will not lead to different
application behavior overlooked on upgrading? Also there is no way to
opt-in new behavior.
Yes, I would be in favor of option 1 and I don't even see a reason not
to do this in PHP 8.4 right away. As I mentioned before, this
effectively can be considered a bug fix: PHP does not correctly round
integers > 2^53.
While this is not a bug fix that is appropriate for the current stable
branches, I don't see why it would not be appropriate for PHP 8.4 if
there's an RFC for it.
In fact there is already a rounding bugfix (no RFC for that) in PHP 8.4:
https://github.com/php/php-src/blob/65a8c70f93ccb7e008de147cd4c357681c653bd0/UPGRADING#L75-L82
While PHP tries hard to keep backwards compatibility within a given
major, keeping full compatibility is effectively impossible for the huge
API surface PHP exposes. Every version includes some changes that are
technically breaking for one reason or another.
Best regards
Tim Düsterhus
Hi Tim,
Hi
As far as I understand you are in favor of option 1. (considering it a
bugfix) but how do you make sure this will not lead to different
application behavior overlooked on upgrading? Also there is no way to
opt-in new behavior.Yes, I would be in favor of option 1 and I don't even see a reason not
to do this in PHP 8.4 right away. As I mentioned before, this
effectively can be considered a bug fix: PHP does not correctly round
integers > 2^53.While this is not a bug fix that is appropriate for the current stable
branches, I don't see why it would not be appropriate for PHP 8.4 if
there's an RFC for it.In fact there is already a rounding bugfix (no RFC for that) in PHP 8.4:
https://github.com/php/php-src/blob/65a8c70f93ccb7e008de147cd4c357681c653bd0/UPGRADING#L75-L82
While PHP tries hard to keep backwards compatibility within a given
major, keeping full compatibility is effectively impossible for the
huge API surface PHP exposes. Every version includes some changes
that are technically breaking for one reason or another.
Fixing edge cases like having 0.49999999999999994 rounded down correctly
is a different story than having something simple like "round(10 / 2)
=== 5.0" to return false instead.
Best regards
Tim Düsterhus
Best,
Marc
While this is not a bug fix that is appropriate for the current stable
branches, I don't see why it would not be appropriate for PHP 8.4 if
there's an RFC for it.In fact there is already a rounding bugfix (no RFC for that) in PHP 8.4:
https://github.com/php/php-src/blob/65a8c70f93ccb7e008de147cd4c357681c653bd0/UPGRADING#L75-L82
This is however very different case as it is still following the currently
voted rounding RFC so it is a more bug fix.
Changing the return type of a heavily used function is a completely
different matter. It will break for anyone using is_float or expecting the
float in some other way. I realise that there is an implicit conversion so
it's not such a huge problem most of the time but there might be case where
it is an issue.
I don't see it as a bug fix in any way. It's more just an implication of
the return type being a float.
I think we should always weight the implication of the break. The way that
I see it that some small breaks are acceptable for minor version but bigger
breaks should be done only in major version. I would consider this (and
anything changing a return types) as a bigger break.
Cheers
Jakub
Hi,
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-rounding
I tried to make the RFC text a bit more clear.
As I'm not a native English speaker I would be very welcome for wording
suggestions and/or grammar fixes especially for the deprecation message.
About the deprecation - as it's not a classical deprecation but a notice
about upcoming behavior change.
Would it make sense to have an additional vote, if or if not to trigger
it in 8.next?
Marc
Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-rounding
I don't understand the point of the deprecation phase at all.
Frankly, I consider this RFC a bug fix of the current "broken"
implementation, which was broken with 7.0 and the move to 64bit integers.
As such, I would just make the behaviour of returning integers the default
behaviour in 8.4.
If, for some weird reason, people want less accurate numbers, they can
always cast the input to float.
Best regards,
George P. Banyard
Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-roundingI don't understand the point of the deprecation phase at all.
Frankly, I consider this RFC a bug fix of the current "broken"
implementation, which was broken with 7.0 and the move to 64bit integers.
As such, I would just make the behaviour of returning integers the default
behaviour in 8.4.
If, for some weird reason, people want less accurate numbers, they can
always cast the input to float.
I don't see a bug or broken behavior here as these functions were
processing floating point numbers since .... forever.
Best regards,
George P. Banyard
Best,
Marc
I don't see a bug or broken behavior here as these functions were
processing floating point numbers since .... forever.
That's not my point, the point is about the function being broken for large
64bit integers, which your RFC is fixing.
I would also expect most people that actually use ceil/floor/round to dump
the return value of it into a property/value that is expected to be an int.
Which is also compatible for floats that fit in an int (and since 8.2 for
those that have a fractional part a warning is emitted).
I see no benefit in doing this weird approach instead of just changing the
behaviour.
Yes this is a theoretical BC break [1], but unless you can show me an
actual use case that breaks (and even then) I don't think the approach of
this RFC is good.
Best regards,
George P. Banyard
[1] And this is coming from me, who does love finding weird edge cases and
remove them from the language, just look at the 8.3 range()
RFC
Hi George,
I don't see a bug or broken behavior here as these functions were
processing floating point numbers since .... forever.That's not my point, the point is about the function being broken for large
64bit integers, which your RFC is fixing.I would also expect most people that actually use ceil/floor/round to dump
the return value of it into a property/value that is expected to be an int.
Which is also compatible for floats that fit in an int (and since 8.2 for
those that have a fractional part a warning is emitted).I see no benefit in doing this weird approach instead of just changing the
behaviour.
Yes this is a theoretical BC break [1], but unless you can show me an
actual use case that breaks (and even then) I don't think the approach of
this RFC is good.
Running a quick code search /round([^(]+)\s===/ language:PHP on
GitHub finds possible breakages based on strict comparison:
// if width and height are ints
$x = ( $height1 / $width1 ) * $width2;
if ( round( $x ) === round( $height2 ) ) {
// if number is int
public static function isZero($number, $precission = 2)
{
return round($number, $precission) === 0.0;
}
// where ecb is int
return $ecb && round(5000 / $ecb) === 2.0;
George P. Banyard
[1] And this is coming from me, who does love finding weird edge cases and
remove them from the language, just look at the 8.3range()
RFC
Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-rounding
I would personally prefer a new function for rounding integers if anyone
wants to round large integers.
The things is that the current round behaviour is consistent with a way how
floats to int conversion is done for strict types.
<?php
declare(strict_types=1);
function test(float $a) {
echo $a;
}
test(987654321098765432);
So it won't really help that much if this function returns long for long in
cases where the result is passed to another function expecting float.
The main problem that I see with the current approach is that it changes
return type for an edge case that most people are not impacted with. For
example it is quite usual that people had a float value with 0 fraction
which gets json encode to int. When they decode it and use round, the
return type is suddenly int. Even though it's usually not a problem, there
might be some code that expects float and maybe even assert that with
is_float. Such code will break.
On the other hand I see use some case for rounding large integers with
negative precision. But for that to work I think it would be better to have
a special function.
If you really want to make such change to round, then I would be prefer
targeting just 9.0 without any deprecation as I don't think the deprecation
should be informational only and not fixable in the code.
Cheers
Jakub
Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-roundingI would personally prefer a new function for rounding integers if anyone
wants to round large integers.The things is that the current round behaviour is consistent with a way how
floats to int conversion is done for strict types.<?php
declare(strict_types=1);function test(float $a) {
echo $a;
}
test(987654321098765432);So it won't really help that much if this function returns long for long in
cases where the result is passed to another function expecting float.The main problem that I see with the current approach is that it changes
return type for an edge case that most people are not impacted with. For
example it is quite usual that people had a float value with 0 fraction
which gets json encode to int. When they decode it and use round, the
return type is suddenly int. Even though it's usually not a problem, there
might be some code that expects float and maybe even assert that with
is_float. Such code will break.On the other hand I see use some case for rounding large integers with
negative precision. But for that to work I think it would be better to have
a special function.If you really want to make such change to round, then I would be prefer
targeting just 9.0 without any deprecation as I don't think the deprecation
should be informational only and not fixable in the code.Cheers
Jakub
Just to add some nuance, if you are going anywhere near the edges of
ints (e.g., custom encryption prototypes from papers), you generally
know that will happen -- it's math and pretty easy to verify. In those
cases, you likely will be using GMP or some other extension to handle
the large numbers. The point is, I highly doubt people are unknowingly
using round()
with exceptionally large numbers. If they are doing so,
they probably know exactly what they are doing and already handle all
potential edge cases or they use an extension specifically for dealing
with large numbers.
Hi Robert,
Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-roundingI would personally prefer a new function for rounding integers if anyone
wants to round large integers.The things is that the current round behaviour is consistent with a way how
floats to int conversion is done for strict types.<?php
declare(strict_types=1);function test(float $a) {
echo $a;
}
test(987654321098765432);So it won't really help that much if this function returns long for long in
cases where the result is passed to another function expecting float.The main problem that I see with the current approach is that it changes
return type for an edge case that most people are not impacted with. For
example it is quite usual that people had a float value with 0 fraction
which gets json encode to int. When they decode it and use round, the
return type is suddenly int. Even though it's usually not a problem, there
might be some code that expects float and maybe even assert that with
is_float. Such code will break.On the other hand I see use some case for rounding large integers with
negative precision. But for that to work I think it would be better to have
a special function.If you really want to make such change to round, then I would be prefer
targeting just 9.0 without any deprecation as I don't think the deprecation
should be informational only and not fixable in the code.Cheers
Jakub
Just to add some nuance, if you are going anywhere near the edges of
ints (e.g., custom encryption prototypes from papers), you generally
know that will happen -- it's math and pretty easy to verify. In those
cases, you likely will be using GMP or some other extension to handle
the large numbers. The point is, I highly doubt people are unknowingly
usinground()
with exceptionally large numbers. If they are doing so,
they probably know exactly what they are doing and already handle all
potential edge cases or they use an extension specifically for dealing
with large numbers.
This very much depends on where you define your edges.
Basically we are talking about 2^53 to 2^63 which is 1024 times higher
number.
Forcing people into such extensions much earlier than necessary isn't
very helpful either as they all come with it's own downsides as well.
Best,
Marc
Hi Jakub,
Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-roundingI would personally prefer a new function for rounding integers if anyone
wants to round large integers.The things is that the current round behaviour is consistent with a way how
floats to int conversion is done for strict types.<?php
declare(strict_types=1);function test(float $a) {
echo $a;
}
test(987654321098765432);So it won't really help that much if this function returns long for long in
cases where the result is passed to another function expecting float.The main problem that I see with the current approach is that it changes
return type for an edge case that most people are not impacted with. For
example it is quite usual that people had a float value with 0 fraction
which gets json encode to int. When they decode it and use round, the
return type is suddenly int. Even though it's usually not a problem, there
might be some code that expects float and maybe even assert that with
is_float. Such code will break.On the other hand I see use some case for rounding large integers with
negative precision. But for that to work I think it would be better to have
a special function.
Your JSON example is a bit unrelated because if you care about your
types your should have used JSON_RESERVE_ZERO_FRACTION in the first
place else you should not care about int vs float at all.
It's true that passing/returning int to/from a function expecting float
will cast but currently with these rounding functions it's a different
deal as they expect an int|float
instead of just float
. So it's not
cast on passing the argument but the functions itself are casting.
Where another set of functions would avoid the BC break it also would be
against having PHP as a loosely typed languageputting the burden to
everyone caring. I already see hundreds of is_int($v) ? round_int($v) : round($v)
everywhere around.
If you really want to make such change to round, then I would be prefer
targeting just 9.0 without any deprecation as I don't think the deprecation
should be informational only and not fixable in the code.Cheers
Jakub
Best,
Marc
Hi Marc,
Hi Jakub,
On Tue, Sep 26, 2023 at 11:39 AM Marc Bennewitz marc@mabe.berlin
wrote:Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-roundingI would personally prefer a new function for rounding integers if anyone
wants to round large integers.The things is that the current round behaviour is consistent with a way
how
floats to int conversion is done for strict types.<?php
declare(strict_types=1);function test(float $a) {
echo $a;
}
test(987654321098765432);So it won't really help that much if this function returns long for long
in
cases where the result is passed to another function expecting float.The main problem that I see with the current approach is that it changes
return type for an edge case that most people are not impacted with. For
example it is quite usual that people had a float value with 0 fraction
which gets json encode to int. When they decode it and use round, the
return type is suddenly int. Even though it's usually not a problem,
there
might be some code that expects float and maybe even assert that with
is_float. Such code will break.On the other hand I see use some case for rounding large integers with
negative precision. But for that to work I think it would be better to
have
a special function.Your JSON example is a bit unrelated because if you care about your
types your should have used JSON_RESERVE_ZERO_FRACTION in the first
place else you should not care about int vs float at all.
The thing is that JSON might be encoded by another application that doesn't
care about types that much. In fact in JSON there is no difference between
2.0 and 2. So this is quite possible situation IMHO.
It's true that passing/returning int to/from a function expecting float
will cast but currently with these rounding functions it's a different
deal as they expect anint|float
instead of justfloat
. So it's not
cast on passing the argument but the functions itself are casting.
Well internally yeah but effectively it is a cast on the return type. What
I meant is that this wont help everywhere when it's passed to function
expecting just float unless it is changed by user to accept int|float.
Where another set of functions would avoid the BC break it also would be
against having PHP as a loosely typed languageputting the burden to
everyone caring. I already see hundreds ofis_int($v) ? round_int($v) : round($v)
everywhere around.
But for me it's sort of an edge as I would think it is not that usual
rounding values that are greater than 2^53. I think it would be good to
note in the RFC what other language do about this issue. I quickly checked
Go and it seems to only have float64 rounding (Math.Round). Rust seems to
also round just f64.
I'm starting to feel that the problem is that the input type is defined as
int|float. I think we should just correct it to float.
Regards
Jakub
Hi Jakub,
Your JSON example is a bit unrelated because if you care about your
types your should have used JSON_RESERVE_ZERO_FRACTION in the first
place else you should not care about int vs float at all.The thing is that JSON might be encoded by another application that doesn't
care about types that much. In fact in JSON there is no difference between
2.0 and 2. So this is quite possible situation IMHO.
That's obviously true but on the same time you should validate and
filter your input data. And if you expect a floating point number you
need to handle that case explicitly before processing and not leave it
to an implicit transformation just hoping everything will be fine.
It's true that passing/returning int to/from a function expecting float
will cast but currently with these rounding functions it's a different
deal as they expect anint|float
instead of justfloat
. So it's not
cast on passing the argument but the functions itself are casting.Well internally yeah but effectively it is a cast on the return type. What
I meant is that this wont help everywhere when it's passed to function
expecting just float unless it is changed by user to accept int|float.Where another set of functions would avoid the BC break it also would be
against having PHP as a loosely typed languageputting the burden to
everyone caring. I already see hundreds ofis_int($v) ? round_int($v) : round($v)
everywhere around.But for me it's sort of an edge as I would think it is not that usual
rounding values that are greater than 2^53. I think it would be good to
note in the RFC what other language do about this issue. I quickly checked
Go and it seems to only have float64 rounding (Math.Round). Rust seems to
also round just f64.
At least python's build-in round function keeps the input type, as long
as ndigits is given, else it even goes further and returns an int.
I'm starting to feel that the problem is that the input type is defined as
int|float. I think we should just correct it to float.
With the current behavior ... yes, but this does not make these
functions any better.
Sure, I'm trying to improve a case for that does not happen often, but
it happens at least for me on working with representing statistical data
where using GMP/bcmath ... does not outweigh it's overhead. On the same
time the results can be improved without adding overhead.
Regards
Jakub
Best,
Marc
Hi,
Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-roundingBest,
Marc
Based on the discussion I see two groups. The one that would like to
change this behavior without message (bug fix) and others that see a to
big BC break never willing to touch it at all.
On the same time my current approach of finding a compromise by
introducing an upcoming behavior change as deprecation message doesn't
make anyone happy.
This makes me go back and collect other ideas:
- Argument $force_float
* $force_float=true in PHP 8.4 + deprecation on passing int and
omitting this new argument
* $force_float=false in PHP 9.0
This allows to opt-in new behavior asap, warns about upcoming behavior
change on passing integers and allows a clean way to fix the deprecation.
But do we would like to keep this argument forever? If not it requires
another deprecation phase forcing people again to fix deprecation messages.
Would it be more logical for $force_float=false
to return the best
possible mathematical correct value instead of trying to keep the input
type if possible? Means a float input could also be cast to int if int
is a better fit in this case.
- Integer specific function
* round_int
This also would allow opt-in proposed behavior without BC break.
But this comes with a couple of downsides:
* Does not improve out-of-the-box behavior
* Requires type-checks before opt-in and goes against PHP as a
loosely typed language
* New inconsistency on function naming round
vs. round_int
- or
just another alias
* New questionable behavior - Why does round_int
return a float in
some cases? Or should this be an error now?
* No logical ceil/floor variant - using these requires type-checks to
not loose integer precision
For me this is a no-go, I wouldn't vote for it and obviously wouldn't
propose this in my own RFC.
- Any other ideas for making all happy?
Best,
Marc
Hi all,
I now have updated the RFC to allow to opt-in to new behavior on PHP 8.4
and be able to opt-out to previous behavior in PHP 9.0 via new parameter
"force_float".
Also the deprecation has been removed as nobody liked it.
Best regards,
Marc
Hi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-roundingBest,
Marc
Hi internals,
Just want to resent this note about the updated RFC as there where no
comments last time and I'm unsure if this was due to mailing list issues.
I now have updated the RFC to allow to opt-in to new behavior on PHP 8.4
and be able to opt-out to previous behavior in PHP 9.0 via new parameter
"force_float".
Also the deprecation has been removed as nobody liked it.
If no further comments arrive I'll like to start voting next week Friday.
Best regards,
Marc
Hi all,
I now have updated the RFC to allow to opt-in to new behavior on PHP
8.4 and be able to opt-out to previous behavior in PHP 9.0 via new
parameter "force_float".Also the deprecation has been removed as nobody liked it.
Best regards,
MarcHi internals
I'd like to put a new RFC under discussion:
https://wiki.php.net/rfc/integer-roundingBest,
Marc
Hi internals,
Just want to resent this note about the updated RFC as there where no
comments last time and I'm unsure if this was due to mailing list issues.I now have updated the RFC to allow to opt-in to new behavior on PHP 8.4
and be able to opt-out to previous behavior in PHP 9.0 via new parameter
"force_float".Also the deprecation has been removed as nobody liked it.
If no further comments arrive I'll like to start voting next week Friday.
Best regards,
Marc
I am still opposed to this. Logically, ceil/float/round should be returning ints, not floats. Only returning ints if it was given an int is, er, kinda pointless, as you'll just get back the value you passed in. (Because it's already rounded/floored, etc.) So this doesn't get us any new type safety, but does make the return type less consistent than it is today. That's a step backwards.
If there's some math reason that we cannot have those functions return int (someone mentioned there was, but I don't really understand it and the RFC does not explain it at all), then we should at least keep consistency in the return type. "Sometimes I have to cast the return value before I can actually use it in the obvious way, sometimes I don't" is not a good situation.
--Larry Garfield
]> If there's some math reason that we cannot have those functions return
int (someone mentioned there was, but I don't really understand it and
the RFC does not explain it at all), then we should at least keep
consistency in the return type. "Sometimes I have to cast the return
value before I can actually use it in the obvious way, sometimes I
don't" is not a good situation.
The round()
function has to be able to return float because has an optional precision parameter. So round(3.14, 1) returns 3.1.
I agree that there doesn't appear to be much of a reason that ceil()
and floor()
should be returning float. The documentation for ceil()
says: The return value of ceil()
is still of type float as the value range of float is usually bigger than that of int.
Jim
I am still opposed to this. Logically, ceil/float/round should be returning ints, not floats. Only returning ints if it was given an int is, er, kinda pointless, as you'll just get back the value you passed in. (Because it's already rounded/floored, etc.) So this doesn't get us any new type safety, but does make the return type less consistent than it is today. That's a step backwards.
If there's some math reason that we cannot have those functions return int (someone mentioned there was, but I don't really understand it and the RFC does not explain it at all), then we should at least keep consistency in the return type. "Sometimes I have to cast the return value before I can actually use it in the obvious way, sometimes I don't" is not a good situation.
--Larry Garfield
There are plenty of values that are exactly representable as floating point numbers but not as integers.
One short example:
$v = 1e10 + 0.6;
var_dump($v);
var_dump(round($v));
Gives you accurate precision and proper rounding behaviour.
1e10 cannot be represented as an integer.
So round must be able to return a float.
Best regards,
Gina P. Banyard
On Saturday, 9 March 2024 at 16:00, Larry Garfield
larry@garfieldtech.com wrote:I am still opposed to this. Logically, ceil/float/round should be returning ints, not floats. Only returning ints if it was given an int is, er, kinda pointless, as you'll just get back the value you passed in. (Because it's already rounded/floored, etc.) So this doesn't get us any new type safety, but does make the return type less consistent than it is today. That's a step backwards.
If there's some math reason that we cannot have those functions return int (someone mentioned there was, but I don't really understand it and the RFC does not explain it at all), then we should at least keep consistency in the return type. "Sometimes I have to cast the return value before I can actually use it in the obvious way, sometimes I don't" is not a good situation.
--Larry Garfield
There are plenty of values that are exactly representable as floating
point numbers but not as integers.One short example:
$v = 1e10 + 0.6;
var_dump($v);
var_dump(round($v));Gives you accurate precision and proper rounding behaviour.
1e10 cannot be represented as an integer.
So round must be able to return a float.
Best regards,
Gina P. Banyard
Please include some version of this in the RFC. Especially if it can be even more detailed.
--Larry Garfield
Hi Larry,
On Saturday, 9 March 2024 at 16:00, Larry Garfield
larry@garfieldtech.com wrote:I am still opposed to this. Logically, ceil/float/round should be returning ints, not floats. Only returning ints if it was given an int is, er, kinda pointless, as you'll just get back the value you passed in. (Because it's already rounded/floored, etc.) So this doesn't get us any new type safety, but does make the return type less consistent than it is today. That's a step backwards.
If there's some math reason that we cannot have those functions return int (someone mentioned there was, but I don't really understand it and the RFC does not explain it at all), then we should at least keep consistency in the return type. "Sometimes I have to cast the return value before I can actually use it in the obvious way, sometimes I don't" is not a good situation.
--Larry Garfield
There are plenty of values that are exactly representable as floating
point numbers but not as integers.One short example:
$v = 1e10 + 0.6;
var_dump($v);
var_dump(round($v));Gives you accurate precision and proper rounding behaviour.
1e10 cannot be represented as an integer.
So round must be able to return a float.
Best regards,
Gina P. Banyard
Please include some version of this in the RFC. Especially if it can be even more detailed.--Larry Garfield
While float's get imprecise on represent integers > 2^53, int's can't
represent such a bit range of numbers as floats can. On rounding a
floating point number your already have to deal with the imprecise of
floats in the first place.
That's why rounding operations should take the input type into account
and only cast if the input type can't represent the resulting number to
avoid ending up in overflow or error.
In case you where looking at python where round()
is documented with ...
The return value is an integer if /ndigits/ is omitted or |None|.
Otherwise, the return value has the same type as /number/.
... but in python int is limited only by memory and not by the type
which makes it very different.
I have added a description and updated example in the RFC.
Best,
Marc