将 false/true 转换为 -1/+1 的无分支方法?
Branchless method to convert false/true to -1/+1?
做以下映射的无分支方式是什么?
true -> +1
false -> -1
一个简单的方法是 if b then 1 else -1
但我正在寻找一种避免分支的方法,即如果。
如果相关,我正在使用 Python。
您可以利用 Python 中的类型 bool
是数字这一事实:
>>> True == 1
True
>>> False == 0
True
所以表达式 2 * b - 1
给出了想要的结果:
>>> def without_branching(b):
... return 2 * b - 1
...
>>> without_branching(True)
1
>>> without_branching(False)
-1
然而,这是否真的是有争议的"branchless"。它将被编译为 Python 没有条件跳转的字节码,但字节码解释器肯定会做一些条件跳转以执行它:至少,它必须检查要执行的操作码,操作数的类型*
和 -
有,依此类推。
也许我们可以像这样使用列表:
[None, True, False][1]
# output True
[None, True, False][-1]
# output False
更新:与评论中提到的相反方式:
[-1, 1][int(False)]
# output -1
[-1, 1][int(True)]
# output 1
更新:或者更简单的使用元组而不需要 int() 转换(正如评论中提到的那样):
(-1, 1)[False]
# output -1
(-1, 1)[True]
# output 1
这里是目前评论和答案中发布的解决方案的比较。
我们可以使用dis
模块来查看每种情况下生成的字节码;这证实了没有条件跳转指令(至少在 Python 代码本身),并且还告诉我们一些关于预期性能的信息,因为执行的操作码数量对此有直接影响(尽管它们是不完全相关)。函数调用的数量也与性能相关,因为它们的开销特别高。
@Glannis Clipper 和@kaya3:(-1, 1)[b]
(3 个操作码)
1 0 LOAD_CONST 2 ((-1, 1))
3 LOAD_NAME 0 (b)
6 BINARY_SUBSCR
@HeapOverflow:-(-1)**b
(4 个操作码)
1 0 LOAD_CONST 0 (-1)
2 LOAD_NAME 0 (b)
4 BINARY_POWER
6 UNARY_NEGATIVE
@HeapOverflow:b - (not b)
(4 个操作码)
1 0 LOAD_NAME 0 (b)
2 LOAD_NAME 0 (b)
4 UNARY_NOT
6 BINARY_SUBTRACT
@kaya3:2 * b - 1
(5 个操作码)
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 个操作码)
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 个操作码)
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 个操作码)
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 个操作码,1 个函数调用)
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 个操作码,1 个函数调用)
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 个操作码,1 个函数调用)
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
做以下映射的无分支方式是什么?
true -> +1
false -> -1
一个简单的方法是 if b then 1 else -1
但我正在寻找一种避免分支的方法,即如果。
如果相关,我正在使用 Python。
您可以利用 Python 中的类型 bool
是数字这一事实:
>>> True == 1
True
>>> False == 0
True
所以表达式 2 * b - 1
给出了想要的结果:
>>> def without_branching(b):
... return 2 * b - 1
...
>>> without_branching(True)
1
>>> without_branching(False)
-1
然而,这是否真的是有争议的"branchless"。它将被编译为 Python 没有条件跳转的字节码,但字节码解释器肯定会做一些条件跳转以执行它:至少,它必须检查要执行的操作码,操作数的类型*
和 -
有,依此类推。
也许我们可以像这样使用列表:
[None, True, False][1]
# output True
[None, True, False][-1]
# output False
更新:与评论中提到的相反方式:
[-1, 1][int(False)]
# output -1
[-1, 1][int(True)]
# output 1
更新:或者更简单的使用元组而不需要 int() 转换(正如评论中提到的那样):
(-1, 1)[False]
# output -1
(-1, 1)[True]
# output 1
这里是目前评论和答案中发布的解决方案的比较。
我们可以使用dis
模块来查看每种情况下生成的字节码;这证实了没有条件跳转指令(至少在 Python 代码本身),并且还告诉我们一些关于预期性能的信息,因为执行的操作码数量对此有直接影响(尽管它们是不完全相关)。函数调用的数量也与性能相关,因为它们的开销特别高。
@Glannis Clipper 和@kaya3:(-1, 1)[b]
(3 个操作码)
1 0 LOAD_CONST 2 ((-1, 1))
3 LOAD_NAME 0 (b)
6 BINARY_SUBSCR
@HeapOverflow:-(-1)**b
(4 个操作码)
1 0 LOAD_CONST 0 (-1)
2 LOAD_NAME 0 (b)
4 BINARY_POWER
6 UNARY_NEGATIVE
@HeapOverflow:b - (not b)
(4 个操作码)
1 0 LOAD_NAME 0 (b)
2 LOAD_NAME 0 (b)
4 UNARY_NOT
6 BINARY_SUBTRACT
@kaya3:2 * b - 1
(5 个操作码)
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 个操作码)
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 个操作码)
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 个操作码)
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 个操作码,1 个函数调用)
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 个操作码,1 个函数调用)
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 个操作码,1 个函数调用)
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