Search code examples
pythonperformanceint

Which is the efficient way to convert a float into an int in python?


I've been using n = int(n) to convert a float into an int.

Recently, I came across another way to do the same thing :

n = n // 1

Which is the most efficient way, and why?


Solution

  • Test it with timeit:

    $ bin/python -mtimeit -n10000000 -s 'n = 1.345' 'int(n)'
    10000000 loops, best of 3: 0.234 usec per loop
    $ bin/python -mtimeit -n10000000 -s 'n = 1.345' 'n // 1'
    10000000 loops, best of 3: 0.218 usec per loop
    

    So floor division is only a faster by a small margin. Note that these values are very close, and I had to crank up the loop repeat count to iron out random influences on my machine. Even with such a high count, you need to repeat the experiments a few times to see how much the numbers still vary and what comes out faster most of the time.

    This is logical, as int() requires a global lookup and a function call (so state is pushed and popped):

    >>> import dis
    >>> def use_int(n):
    ...     return int(n)
    ... 
    >>> def use_floordiv(n):
    ...     return n // 1
    ... 
    >>> dis.dis(use_int)
      2           0 LOAD_GLOBAL              0 (int)
                  3 LOAD_FAST                0 (n)
                  6 CALL_FUNCTION            1
                  9 RETURN_VALUE        
    >>> dis.dis(use_floordiv)
      2           0 LOAD_FAST                0 (n)
                  3 LOAD_CONST               1 (1)
                  6 BINARY_FLOOR_DIVIDE 
                  7 RETURN_VALUE        
    

    It is the LOAD_GLOBAL and CALL_FUNCTION opcodes that are slower than the LOAD_CONST and BINARY_FLOOR_DIVIDE opcodes; LOAD_CONST is a simple array lookup, LOAD_GLOBAL needs to do a dictionary lookup instead.

    Binding int() to a local name can make a small difference, giving it the edge again (as it has to do less work than // 1 floor division):

    $ bin/python -mtimeit -n10000000 -s 'n = 1.345' 'int(n)'
    10000000 loops, best of 3: 0.233 usec per loop
    $ bin/python -mtimeit -n10000000 -s 'n = 1.345; int_=int' 'int_(n)'
    10000000 loops, best of 3: 0.195 usec per loop
    $ bin/python -mtimeit -n10000000 -s 'n = 1.345' 'n // 1'
    10000000 loops, best of 3: 0.225 usec per loop
    

    Again, you need to run this with 10 million loops to see the differences consistently.

    That said, int(n) is a lot more explicit and unless you are doing this in a time-critical loop, int(n) wins it in readability over n // 1. The timing differences are too small to make the cognitive cost of having to work out what // 1 does here worthwhile.