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?
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.