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: Unexpected behaviour when converting large float to int
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.5
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Nicolas Primeau, eryksun
Priority: normal Keywords:

Created on 2015-10-09 19:54 by Nicolas Primeau, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (2)
msg252642 - (view) Author: Nicolas Primeau (Nicolas Primeau) Date: 2015-10-09 19:54
Converting arbitrarily large float type numbers to integer type number results in a non trivially different number.

This can be repeated in all Python versions, same with Python 2 with longs.

Expected behaviour would be an integer representation of 1e+44, and some_number == int_version.

Example:
>>> some_number = 100000000000000000000000000000000000000000000
>>> type(some_number)
<class 'int'>
>>> float_version = float(some_number)
>>> float_version
1e+44
>>> type(float_version)
<class 'float'>
>>> int_version = int(float_version)
>>> type(int_version)
<class 'int'>
>>> int_version
100000000000000008821361405306422640701865984
>>> int_version == some_number
False
>>> int_version == float_version
True
msg252653 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2015-10-09 20:50
int(float_version) is returning the actual integer value represented by the float, which is the closest approximation possible using an [IEEE 754 binary64][1], i.e. a C double. Here's one way to compute this value manually:

    from struct import pack
    from fractions import Fraction

    some_number = 1 * 10**44
    float_version = float(some_number)

    n = int.from_bytes(pack('>d', float_version), 'big')
    sign = n >> 63
    exp = ((n >> 52) & (2**11 - 1)) - 1023
    nmantissa = n & (2**52 - 1)
    significand = 1 + sum(Fraction((nmantissa >> (52 - i)) & 1, 2**i)
                          for i in range(52))

    >>> (-1)**sign * significand * 2**exp
    Fraction(100000000000000008821361405306422640701865984, 1)

Or just use the as_integer_ratio method:

    >>> float_version.as_integer_ratio()
    (100000000000000008821361405306422640701865984, 1)

[1]: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
History
Date User Action Args
2022-04-11 14:58:22adminsetgithub: 69545
2015-10-09 20:50:45eryksunsetstatus: open -> closed

nosy: + eryksun
messages: + msg252653

resolution: not a bug
stage: resolved
2015-10-09 19:54:20Nicolas Primeaucreate