This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: math.modf() change integer returned part as integer instead of float
Type: behavior Stage: resolved
Components: Library (Lib) Versions:
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: aikimark1955, mark.dickinson, rhettinger, serhiy.storchaka, tim.peters
Priority: normal Keywords:

Created on 2019-11-15 15:02 by aikimark1955, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (8)
msg356680 - (view) Author: aikimark1955 (aikimark1955) Date: 2019-11-15 15:02
The description for this function reads:
"Return the fractional and integer parts of x. Both results carry the sign of x and are floats."

Since the second returned value is "integer parts", I submit that the value should be integer.
msg356686 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-11-15 16:31
This is odd.  The underlying C library modf() returns an integer for the second part.  Likewise the Python math.frexp() function returns an integer for the second part.  So, I'm not sure why Python's math.modf() returns a float for the second field.
msg356707 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2019-11-15 19:14
Raymond, I think you've tricked yourself ;-)  The prototype for C's duoble modf is

double modf(double x, double *intptr);

Yes, after the call *intptr is an exact integer, but in the double format.

>>> math.modf(1e300)
(0.0, 1e+300)

I don't think it would be doing anyone a real favor by returning

1000000000000000052504760255204420248704468581108159154915854115511802457988908195786371375080447864043704443832883878176942523235360430575644792184786706982848387200926575803737830233794788090059368953234970799945081119038967640880074652742780142494579258788820056842838115669472196386865459400540160

instead of 1e300 for "the integer part".

`frexp()` is very different, in that the second part is a (small!) integer exponent - a float would work for that too, but would be surprising.

>>> math.frexp(1e300)
(0.7466108948025751, 997)

A better analogy would be to math.floor(), which does return an int, even for cases like floor(1e300).  In Python 2 it did not (it returned an integer but in float format), and I believe it was a mistake to change it to return an int.  Building giant ints is not what C does, so is surprising on that count alone, and is far more expensive than returning (as C does) a float.

That is, we didn't improve on C's floor, we introduced a surprising difference that sometimes hurts and rarely helps (if anyone really wanted an int, they could apply int() to the float result).
msg356725 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-11-15 23:20
> Raymond, I think you've tricked yourself ;-)

Yup.
msg356748 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-11-16 12:09
[Tim]

> I don't think it would be doing anyone a real favor by returning [...]

Agreed.

> math.floor(), which does return an int, [...] and I believe it was a mistake to change it to return an int.

Also agreed.
msg356749 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-11-16 12:38
> math.floor(), which does return an int, [...] and I believe it was a mistake to change it to return an int.

What about math.trunc() and round()? Should they return int or float?

And what about the result of the floor division of floats? Should it be int or float?
msg356751 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-11-16 15:44
[Serhiy]

> What about math.trunc() and round()? Should they return int or float?

math.trunc was deliberately introduced to be an explicitly-value-modifying conversion to int (as opposed to "int" itself, which is used for both lossy and lossless conversions to int); so from that perspective it should return int.

round is less problematic than floor and ceil because it provides a way to preserve the type (by using `0` as the second argument).

Single-argument round frequently turns out to be the right way to convert a float to an int in practice (e.g., in something like `nsteps = round((stop - start) / step)`, in a case where you know in advance that mathematically stop - start is a small integer multiple of step, but floating-point errors might cause the quotient to deviate from that integer by a small amount in either direction), so I've often appreciated the convenience of being able to use `round(some_float)` instead of `int(round(some_float))`.

Floor division of floats already returns a float, and I can't see a strong reason to change that. It's not exactly the most useful floating-point operation in the first place, so it's hard to find use-cases that argue for one return type over the other.

But I think all of this is academic: it's another case of "Should we have done this differently in the first place?" versus "Should we change it now?". The answer to the first question *might* be "Yes" for some pieces, but the answer to the second is almost certainly "No".
msg356752 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-11-16 15:48
Thanks for the suggestion. I'm closing this as rejected, for the reasons Tim gave.
History
Date User Action Args
2022-04-11 14:59:23adminsetgithub: 82994
2019-11-16 15:48:51mark.dickinsonsetstatus: open -> closed
resolution: rejected
messages: + msg356752

stage: resolved
2019-11-16 15:44:18mark.dickinsonsetmessages: + msg356751
2019-11-16 12:38:04serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg356749
2019-11-16 12:09:37mark.dickinsonsetmessages: + msg356748
2019-11-15 23:20:45rhettingersetmessages: + msg356725
2019-11-15 19:14:36tim.peterssetmessages: + msg356707
2019-11-15 16:31:52rhettingersetnosy: + rhettinger, mark.dickinson, tim.peters
messages: + msg356686
2019-11-15 15:02:01aikimark1955create