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.remainder() give wrong answer on large integer
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.8
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: David Hwang, steven.daprano, tim.peters
Priority: normal Keywords:

Created on 2020-02-02 05:10 by David Hwang, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (3)
msg361211 - (view) Author: David Hwang (David Hwang) Date: 2020-02-02 05:10
These two numbers are off by 1, and so should give different answer to
>>> math.remainder(12345678901234567890,3)
1.0
>>> math.remainder(12345678901234567891,3)
1.0
msg361214 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2020-02-02 05:42
math.remainder performs *floating point* remainder. That means it has to convert the arguments to floats first, and there is no float capable of representing either of those two numbers exactly:

    py> '%f' % float(12345678901234567890)
    '12345678901234567168.000000'
    py> '%f' % float(12345678901234567891)
    '12345678901234567168.000000'

That's just the way floats work. They don't have infinite precision, so sometimes they have rounding error.

If you want exact integer remainder, use the `%` operator:

    py> 12345678901234567890 % 3
    0
    py> 12345678901234567891 % 3
    1

See the tutorial and the FAQs

https://docs.python.org/3/tutorial/floatingpoint.html

https://docs.python.org/3/faq/design.html#why-are-floating-point-calculations-so-inaccurate
msg361217 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2020-02-02 05:54
Arguments to `remainder()` are converted to floats, and the returned value is also a float.  These specific arguments convert to the same float:

>>> a = 12345678901234567890
>>> b = 12345678901234567891
>>> float(a) == float(b)
True

And the float they convert _to_ retains only the 53 most-significant bits of the inputs:

>>> int(float(b))
12345678901234567168
>>> c = _
>>> c.bit_length()
64
>>> bin(c)
'0b1010101101010100101010011000110011101011000111110000100000000000'

Note that the last 11 bits are zeroes, and 64 - 11 = 53.

So this is all doing what it's intended to do, and won't be changed.

I'm leaving this open, though, in case someone thinks the docs should be clarified.  But the same things apply to _many_ functions in the `math` module:  unless the docs specifically say they work with integer arguments, integer arguments are converted to float.

Oops!  Someone else already closed this while I was typing.  That's fine by me too ;-)
History
Date User Action Args
2022-04-11 14:59:26adminsetgithub: 83706
2020-02-02 05:54:12tim.peterssetnosy: + tim.peters
messages: + msg361217
2020-02-02 05:42:11steven.dapranosetstatus: open -> closed

nosy: + steven.daprano
messages: + msg361214

resolution: not a bug
stage: resolved
2020-02-02 05:10:48David Hwangcreate