问题
What is a branchless way to do the following mapping?
true -> +1
false -> -1
An easy way would be if b then 1 else -1
but I'm looking for a method to avoid the branch, i.e. if.
If it is relevant, I'm using Python.
回答1:
You can exploit the fact that in Python, the type bool
is numeric:
>>> True == 1
True
>>> False == 0
True
So the expression 2 * b - 1
gives the desired results:
>>> def without_branching(b):
... return 2 * b - 1
...
>>> without_branching(True)
1
>>> without_branching(False)
-1
However, it's arguable whether even this is really "branchless". It will be compiled to Python bytecode with no conditional jumps, but the bytecode interpreter will certainly do some conditional jumps in order to execute it: at the very least, it has to check which opcodes to execute, what types the operands of *
and -
have, and so on.
回答2:
Here's a comparison of the solutions posted in comments and answers so far.
We can use the dis module to see the generated bytecode in each case; this confirms that there are no conditional jump instructions (in the Python code itself, at least), and also tells us something about the expected performance, since the number of opcodes executed has a direct impact on that (though they are not perfectly correlated). The number of function calls is also relevant for performance, since these have a particularly high overhead.
@Glannis Clipper and @kaya3: (-1, 1)[b]
(3 opcodes)
1 0 LOAD_CONST 2 ((-1, 1))
3 LOAD_NAME 0 (b)
6 BINARY_SUBSCR
@HeapOverflow: -(-1)**b
(4 opcodes)
1 0 LOAD_CONST 0 (-1)
2 LOAD_NAME 0 (b)
4 BINARY_POWER
6 UNARY_NEGATIVE
@HeapOverflow: b - (not b)
(4 opcodes)
1 0 LOAD_NAME 0 (b)
2 LOAD_NAME 0 (b)
4 UNARY_NOT
6 BINARY_SUBTRACT
@kaya3: 2 * b - 1
(5 opcodes)
1 0 LOAD_CONST 0 (2)
3 LOAD_NAME 0 (b)
6 BINARY_MULTIPLY
7 LOAD_CONST 1 (1)
10 BINARY_SUBTRACT
@HeapOverflow: ~b ^ -b
(5 opcodes)
1 0 LOAD_NAME 0 (b)
2 UNARY_INVERT
4 LOAD_NAME 0 (b)
6 UNARY_NEGATIVE
8 BINARY_XOR
@Mark Meyer: b - (b - 1) * -1
(7 opcodes)
1 0 LOAD_NAME 0 (b)
3 LOAD_NAME 0 (b)
6 LOAD_CONST 0 (1)
9 BINARY_SUBTRACT
10 LOAD_CONST 1 (-1)
13 BINARY_MULTIPLY
14 BINARY_SUBTRACT
@Sayse: {True: 1, False: -1}[b]
(7 opcodes)
1 0 LOAD_CONST 0 (True)
3 LOAD_CONST 1 (1)
6 LOAD_CONST 2 (False)
9 LOAD_CONST 3 (-1)
12 BUILD_MAP 2
15 LOAD_NAME 0 (b)
18 BINARY_SUBSCR
@deceze: {True: 1}.get(b, -1)
(7 opcodes, 1 function call)
1 0 LOAD_CONST 0 (True)
3 LOAD_CONST 1 (1)
6 BUILD_MAP 1
9 LOAD_ATTR 0 (get)
12 LOAD_NAME 1 (b)
15 LOAD_CONST 2 (-1)
18 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
@Glannis Clipper: [-1, 1][int(b)]
(7 opcodes, 1 function call)
1 0 LOAD_CONST 1 (-1)
3 LOAD_CONST 0 (1)
6 BUILD_LIST 2
9 LOAD_NAME 0 (int)
12 LOAD_NAME 1 (b)
15 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
18 BINARY_SUBSCR
@divyang4481: 2 * int(b) - 1
(7 opcodes, 1 function call)
1 0 LOAD_CONST 0 (2)
3 LOAD_NAME 0 (int)
6 LOAD_NAME 1 (b)
9 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
12 BINARY_MULTIPLY
13 LOAD_CONST 1 (1)
16 BINARY_SUBTRACT
回答3:
Maybe we can use a list in a way like this:
[None, True, False][1]
# output True
[None, True, False][-1]
# output False
UPDATE: And the opposite way as mentioned in comments:
[-1, 1][int(False)]
# output -1
[-1, 1][int(True)]
# output 1
UPDATE: Or even simpler with the use of a tuple and without the need of int() conversion (as mentioned in comments too):
(-1, 1)[False]
# output -1
(-1, 1)[True]
# output 1
来源:https://stackoverflow.com/questions/59912130/branchless-method-to-convert-false-true-to-1-1