Safest way to convert float to integer in python?

758,966

Solution 1

All integers that can be represented by floating point numbers have an exact representation. So you can safely use int on the result. Inexact representations occur only if you are trying to represent a rational number with a denominator that is not a power of two.

That this works is not trivial at all! It's a property of the IEEE floating point representation that int∘floor = ⌊⋅⌋ if the magnitude of the numbers in question is small enough, but different representations are possible where int(floor(2.3)) might be 1.

To quote from Wikipedia,

Any integer with absolute value less than or equal to 224 can be exactly represented in the single precision format, and any integer with absolute value less than or equal to 253 can be exactly represented in the double precision format.

Solution 2

Use int(your non integer number) will nail it.

print int(2.3) # "2"
print int(math.sqrt(5)) # "2"

Solution 3

You could use the round function. If you use no second parameter (# of significant digits) then I think you will get the behavior you want.

IDLE output.

>>> round(2.99999999999)
3
>>> round(2.6)
3
>>> round(2.5)
3
>>> round(2.4)
2

Solution 4

Combining two of the previous results, we have:

int(round(some_float))

This converts a float to an integer fairly dependably.

Solution 5

That this works is not trivial at all! It's a property of the IEEE floating point representation that int∘floor = ⌊⋅⌋ if the magnitude of the numbers in question is small enough, but different representations are possible where int(floor(2.3)) might be 1.

This post explains why it works in that range.

In a double, you can represent 32bit integers without any problems. There cannot be any rounding issues. More precisely, doubles can represent all integers between and including 253 and -253.

Short explanation: A double can store up to 53 binary digits. When you require more, the number is padded with zeroes on the right.

It follows that 53 ones is the largest number that can be stored without padding. Naturally, all (integer) numbers requiring less digits can be stored accurately.

Adding one to 111(omitted)111 (53 ones) yields 100...000, (53 zeroes). As we know, we can store 53 digits, that makes the rightmost zero padding.

This is where 253 comes from.


More detail: We need to consider how IEEE-754 floating point works.

  1 bit    11 / 8     52 / 23      # bits double/single precision
[ sign |  exponent | mantissa ]

The number is then calculated as follows (excluding special cases that are irrelevant here):

-1sign × 1.mantissa ×2exponent - bias

where bias = 2exponent - 1 - 1, i.e. 1023 and 127 for double/single precision respectively.

Knowing that multiplying by 2X simply shifts all bits X places to the left, it's easy to see that any integer must have all bits in the mantissa that end up right of the decimal point to zero.

Any integer except zero has the following form in binary:

1x...x where the x-es represent the bits to the right of the MSB (most significant bit).

Because we excluded zero, there will always be a MSB that is one—which is why it's not stored. To store the integer, we must bring it into the aforementioned form: -1sign × 1.mantissa ×2exponent - bias.

That's saying the same as shifting the bits over the decimal point until there's only the MSB towards the left of the MSB. All the bits right of the decimal point are then stored in the mantissa.

From this, we can see that we can store at most 52 binary digits apart from the MSB.

It follows that the highest number where all bits are explicitly stored is

111(omitted)111.   that's 53 ones (52 + implicit 1) in the case of doubles.

For this, we need to set the exponent, such that the decimal point will be shifted 52 places. If we were to increase the exponent by one, we cannot know the digit right to the left after the decimal point.

111(omitted)111x.

By convention, it's 0. Setting the entire mantissa to zero, we receive the following number:

100(omitted)00x. = 100(omitted)000.

That's a 1 followed by 53 zeroes, 52 stored and 1 added due to the exponent.

It represents 253, which marks the boundary (both negative and positive) between which we can accurately represent all integers. If we wanted to add one to 253, we would have to set the implicit zero (denoted by the x) to one, but that's impossible.

Share:
758,966

Related videos on Youtube

Boaz
Author by

Boaz

Updated on October 30, 2021

Comments

  • Boaz
    Boaz over 2 years

    Python's math module contain handy functions like floor & ceil. These functions take a floating point number and return the nearest integer below or above it. However these functions return the answer as a floating point number. For example:

    import math
    f=math.floor(2.3)
    

    Now f returns:

    2.0
    

    What is the safest way to get an integer out of this float, without running the risk of rounding errors (for example if the float is the equivalent of 1.99999) or perhaps I should use another function altogether?

    • sancho.s ReinstateMonicaCellio
      sancho.s ReinstateMonicaCellio about 8 years
      math.floor returns a float in v2.6, but it returns an integer in v3. At this point (almost six years after the OP), this issue might show up rarely.
    • Vincenzooo
      Vincenzooo over 5 years
      however numpy still returns float, so the question is valid.
    • WurmD
      WurmD over 3 years
      The title could be improved to "Safest way to round down and convert float to integer in python?"
  • Gordon Gustafson
    Gordon Gustafson almost 14 years
    +1 for going a bit deeper. You could also throw in a brief explanation as to why: en.wikipedia.org/wiki/Floating_point :D
  • Philipp
    Philipp almost 14 years
    round returns a float number as well, at least in Python 2.6.
  • robert
    robert almost 14 years
    In Python 3.1.2, round returns an int.
  • Philipp
    Philipp almost 14 years
    Indeed, both round and floor return integers in Python 3.x. So I suppose that the question concerns Python 2.x.
  • Wayne Werner
    Wayne Werner almost 14 years
    and since (it appears that) the OP is using IPython which isn't available for py3k, that's probably a valid supposition, Phillip.
  • Bill Rosmus
    Bill Rosmus almost 11 years
    From: docs.python.org/2/library/math.html - math.floor(x) - Return the floor of x as a float, the largest integer value less than or equal to x.
  • jochen
    jochen almost 10 years
    This won't work for negative numbers: floor rounds down whereas int rounds towards 0.
  • srodriguex
    srodriguex almost 10 years
    @jochen I tested int(-2.3) in Python distribution Canopy 2.7.6 and got -2 as expected. Integer numbers can be negative, the same way in formal Math definition.
  • jochen
    jochen almost 10 years
    I agree, int(-2.3) gives -2 as you say, because it rounds towards 0, i.e. up in this case. In contrast, the original question used math.floor, which always rounds down: math.floor(-2.3) gives -3.0.
  • teewuane
    teewuane over 9 years
    so maybe int(round(2.65)) ?
  • Agostino
    Agostino about 9 years
    What happens if you try to round a very long float? Will this at least raise an exception?
  • JMTyler
    JMTyler over 8 years
    That is not really a problem. OP just wants an integer out of the result of math.floor, and this answer shows how to convert a float into an integer. Take the float from math.floor and pipe it through int, problem solved: int(math.floor(2.3))
  • kralyk
    kralyk about 8 years
    @Agostino What do you mean "very long float"?
  • Agostino
    Agostino about 8 years
    @kralyk I mean a float representing a number bigger than what a normal int can hold. In Python 2, are there float values that you can only represent using a long (after rounding)?
  • Agostino
    Agostino about 8 years
    @kralyk you mean, after the round? So, would casting them to int raise an exception, or just truncate them?
  • kralyk
    kralyk about 8 years
    @Agostino No, the int() function produces either an int or a long based on what is needed...
  • kralyk
    kralyk about 8 years
    @Agostino docs.python.org/2/library/functions.html#int , it says "If x is a number, it can be a plain integer, a long integer, or a floating point number." which is what applies in this situation.
  • Agostino
    Agostino about 8 years
    @kralyk thanks, maybe we can clean up the comments and add a note to the answer?
  • kralyk
    kralyk about 8 years
    @Agostino I suppose we can yeah
  • Mayou36
    Mayou36 over 7 years
    Did you even read the question? He is aware of the int() function, but has asked if you may run into trouble with 1.9999 instead of 2.0. Your answer is not even close to an answer at all, you missed the whole point...
  • srodriguex
    srodriguex over 7 years
    @Mayou36 It seems I missed the point, but it's what I wrote with the knowledge I had at the time, so let it be. The community will vote up or down as they see fit. But, I really think it holds: int(-999999999980/99999999999) == -9 # True
  • candh
    candh almost 7 years
    why does round(6.5) gives 6? It seems to kinda ceil() the float when there's an immediate 5 (or greater up to 9) after the decimal in all other cases. Why is this not working in this case? or any other case when the number ends with a six and there's a 5 right after the decimal...
  • Alex
    Alex over 6 years
    Why do you need to do call math.floor when int already does the same thing?
  • Admin
    Admin over 6 years
    @Alex: int and floor return different values for negative numbers, of course.
  • Juan
    Juan over 6 years
    In Python 2, an "int" is the same as a C "int". In Python 3, it appears there is no limit to the size of an "int, stackoverflow.com/questions/13795758/…. The meaning of "int" is also dependent on the operating system and underlying hardware. See en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models. If you are programming with the C-API, python 3 you have to be very careful in what the definition of long and size_t is on your platform. docs.python.org/3/c-api/long.html
  • Nirvana
    Nirvana almost 3 years
    candh I believe it uses "banker's rounding"/"rounding half to even" method– rather than rounding 0.5 and higher up, and 0.4 and lower down, it rounds 0.5 to the nearest even number.
  • Tatarize
    Tatarize over 2 years
    int(float('inf')) raises an Overflow Error.