Branchless method to convert false/true to -1/+1? [closed]

不羁的心 提交于 2021-02-08 12:00:54

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!