使用 Python 的 % 计算 C 的 % ?
Compute C's `%` using Python's `%`?
如何使用 Python 的 %
计算 C 的 %
?
两者之间的区别在于他们处理否定论据的方式。
在这两种语言中,%
的定义方式使得此关系(//
是整数除法)成立:
a // b * b + a % b == a
但是 a // b
的舍入在 C 和 Python 中是不同的,导致 a % b
的不同定义。
例如,在 C 中(其中整数除法只是 /
和 int
操作数)我们有:
int a = 31;
int b = -3;
a / b; // -10
a % b; // 1
在 Python 时:
a = 31
b = -3
a // b # -11
a % b # -2
我知道这个 question,它解决了相反的问题(即如何从 C 的 %
计算 Python 的 %
)并包含其他讨论。
我也知道 Python 3.7 math
模块引入了 remainder()
但它的结果是 float
,而不是 int
因此它会不享受任意精度。
有些方法是:
def mod_c0(a, b):
if b < 0:
b = -b
return -1 * (-a % b) if a < 0 else a % b
def mod_c1(a, b):
return (-1 if a < 0 else 1) * ((a if a > 0 else -a) % (b if b > 0 else -b))
def mod_c2(a, b):
return (-1 if a < 0 else 1) * (abs(a) % abs(b))
def mod_c3(a, b):
r = a % b
return (r - b) if (a < 0) != (b < 0) and r != 0 else r
def mod_c4(a, b):
r = a % b
return (r - b) if (a * b < 0) and r != 0 else r
def mod_c5(a, b):
return a % (-b if a ^ b < 0 else b)
def mod_c6(a, b):
a_xor_b = a ^ b
n = a_xor_b.bit_length()
x = a_xor_b >> n
return a % (b * (x | 1))
def mod_c7(a, b):
a_xor_b = a ^ b
n = a_xor_b.bit_length()
x = a_xor_b >> n
return a % ((-b & x) | (b & ~x))
def mod_c8(a, b):
q, r = divmod(a, b)
if (a >= 0) != (b >= 0) and r:
q += 1
return a - q * b
def mod_c9(a, b):
if a >= 0:
if b >= 0:
return a % b
else:
return a % -b
else:
if b >= 0:
return -(-a % b)
else:
return a % b
一切都按预期工作,例如:
print(mod_c0(31, -3))
# 1
本质上,mod_c0()
实现了 mod_c1()
和 mod_c2()
的优化版本,除了在 mod_c1()
中调用(相对昂贵)调用 abs()
替换为具有相同语义的三元条件运算符。
相反,mod_c3()
和 mod_c4()
尝试在需要的情况下直接修复 a % b
值。两者之间的区别在于它们如何检测参数的相反符号:(a < 0) != (b != 0)
与 a * b < 0
。
mod_c5()
方法受到 , and essentially uses the bit-wise xor to handle the cases correctly, while mod_c6()
and mod_c7()
are the same as 的启发,但使用 int.bit_length()
的自适应右移。
mod_c8()
方法使用更正的整数除法定义来确定模值。
mod_c9()
方法的灵感来自 ,本质上是完全有条件的。
涵盖所有标志案例:
vals = (3, -3, 31, -31)
s = '{:<{n}}' * 4
n = 14
print(s.format('a', 'b', 'mod(a, b)', 'mod_c(a, b)', n=n))
print(s.format(*(('-' * (n - 1),) * 4), n=n))
for a, b in itertools.product(vals, repeat=2):
print(s.format(a, b, mod(a, b), mod_c0(a, b), n=n))
a b mod(a, b) mod_c(a, b)
------------- ------------- ------------- -------------
3 3 0 0
3 -3 0 0
3 31 3 3
3 -31 -28 3
-3 3 0 0
-3 -3 0 0
-3 31 28 -3
-3 -31 -3 -3
31 3 1 1
31 -3 -2 1
31 31 0 0
31 -31 0 0
-31 3 2 -1
-31 -3 -1 -1
-31 31 0 0
-31 -31 0 0
更多测试和基准测试:
n = 100
k = 1
l = [x for x in range(-n, n + k, k)]
ll = [(a, b) for a, b in itertools.product(l, repeat=2) if b]
funcs = mod_c0, mod_c1, mod_c2, mod_c3, mod_c4, mod_c5, mod_c6, mod_c7, mod_c8, mod_c9
for func in funcs:
correct = all(func(a, b) == funcs[0](a, b) for a, b in ll)
print(func.__name__, 'correct:', all_equal)
%timeit [func(a, b) for a, b in ll]
print()
mod_c0 correct: True
100 loops, best of 3: 6.6 ms per loop
mod_c1 correct: True
100 loops, best of 3: 7.86 ms per loop
mod_c2 correct: True
100 loops, best of 3: 8.49 ms per loop
mod_c3 correct: True
100 loops, best of 3: 7.56 ms per loop
mod_c4 correct: True
100 loops, best of 3: 7.5 ms per loop
mod_c5 correct: True
100 loops, best of 3: 7.94 ms per loop
mod_c6 correct: True
100 loops, best of 3: 13.4 ms per loop
mod_c7 correct: True
100 loops, best of 3: 16.8 ms per loop
mod_c8 correct: True
100 loops, best of 3: 12.4 ms per loop
mod_c9 correct: True
100 loops, best of 3: 6.48 ms per loop
也许有更好(更短?、更快?)的方法,因为使用 C 的 %
实现 Python 的 %
似乎更简单:
((a % b) + b) % b
了解 C 风格的 %
计算(上面的 mod_c*()
函数)如何与通常的 %
或获得 [=90= 所需的操作相对立] 风格 %
来自 C
:
def mod_py(a, b):
return a % b
def mod_c2py(a, b):
return ((a % b) + b) % b
%timeit [mod_py(a, b) for a, b in ll]
# 100 loops, best of 3: 5.85 ms per loop
%timeit [mod_c2py(a, b) for a, b in ll]
# 100 loops, best of 3: 7.84 ms per loop
当然请注意,mod_c2py()
仅用于了解我们可以从 mod_c()
函数中获得什么样的性能。
(已编辑 以修复一些建议的方法并包括一些时间安排)
(EDITED-2 添加 mod_c5()
解决方案)
(EDITED-3 将 mod_c6()
添加到 mod_c9()
解决方案)
对于 64 位整数,这些都应该有效:
def mod_c_AA0(a,b):
x=(a^b)>>63
return a % (b*(x|1))
def mod_c_AA1(a,b):
x=(a^b)>>63
return a % ((-b & x)|(b & ~x))
使用二进制补码。正如 norok2 所建议的那样,根据 a
和 b
.
的大小,将第一行替换为 a_xor_b=a^b; x=a_xor_b>>a_xor_b.bit_length();
以获得位移的最佳特异性
我正在跟进@norok2 的非常全面的回答。
我已经尝试过使用分支的超级天真方法,它似乎略有但始终更快 (~2-4%)。
def mod_naive(x,y):
if y < 0:
if x < 0:
return x%y
else:
return (x%-y)
else:
if x < 0:
return -(-x%y)
else:
return x%y
或使用 lambda(不影响速度,仅影响凉爽):
mod_naive = lambda x,y: (x%y if x < 0 else x%-y) if y < 0 else (-(-x%y) if x < 0 else x%y)
与@norok2 最快的解决方案 (mod_c0
) 相比:
mod_c0 correct: True
100 loops, best of 3: 6.86 ms per loop
mod_naive correct: True
100 loops, best of 3: 6.58 ms per loop
我(天真的)猜测分支预测算法最终会产生较少的整体操作。
如何使用 Python 的 %
计算 C 的 %
?
两者之间的区别在于他们处理否定论据的方式。
在这两种语言中,%
的定义方式使得此关系(//
是整数除法)成立:
a // b * b + a % b == a
但是 a // b
的舍入在 C 和 Python 中是不同的,导致 a % b
的不同定义。
例如,在 C 中(其中整数除法只是 /
和 int
操作数)我们有:
int a = 31;
int b = -3;
a / b; // -10
a % b; // 1
在 Python 时:
a = 31
b = -3
a // b # -11
a % b # -2
我知道这个 question,它解决了相反的问题(即如何从 C 的 %
计算 Python 的 %
)并包含其他讨论。
我也知道 Python 3.7 math
模块引入了 remainder()
但它的结果是 float
,而不是 int
因此它会不享受任意精度。
有些方法是:
def mod_c0(a, b):
if b < 0:
b = -b
return -1 * (-a % b) if a < 0 else a % b
def mod_c1(a, b):
return (-1 if a < 0 else 1) * ((a if a > 0 else -a) % (b if b > 0 else -b))
def mod_c2(a, b):
return (-1 if a < 0 else 1) * (abs(a) % abs(b))
def mod_c3(a, b):
r = a % b
return (r - b) if (a < 0) != (b < 0) and r != 0 else r
def mod_c4(a, b):
r = a % b
return (r - b) if (a * b < 0) and r != 0 else r
def mod_c5(a, b):
return a % (-b if a ^ b < 0 else b)
def mod_c6(a, b):
a_xor_b = a ^ b
n = a_xor_b.bit_length()
x = a_xor_b >> n
return a % (b * (x | 1))
def mod_c7(a, b):
a_xor_b = a ^ b
n = a_xor_b.bit_length()
x = a_xor_b >> n
return a % ((-b & x) | (b & ~x))
def mod_c8(a, b):
q, r = divmod(a, b)
if (a >= 0) != (b >= 0) and r:
q += 1
return a - q * b
def mod_c9(a, b):
if a >= 0:
if b >= 0:
return a % b
else:
return a % -b
else:
if b >= 0:
return -(-a % b)
else:
return a % b
一切都按预期工作,例如:
print(mod_c0(31, -3))
# 1
本质上,mod_c0()
实现了 mod_c1()
和 mod_c2()
的优化版本,除了在 mod_c1()
中调用(相对昂贵)调用 abs()
替换为具有相同语义的三元条件运算符。
相反,mod_c3()
和 mod_c4()
尝试在需要的情况下直接修复 a % b
值。两者之间的区别在于它们如何检测参数的相反符号:(a < 0) != (b != 0)
与 a * b < 0
。
mod_c5()
方法受到 mod_c6()
and mod_c7()
are the same as int.bit_length()
的自适应右移。
mod_c8()
方法使用更正的整数除法定义来确定模值。
mod_c9()
方法的灵感来自
涵盖所有标志案例:
vals = (3, -3, 31, -31)
s = '{:<{n}}' * 4
n = 14
print(s.format('a', 'b', 'mod(a, b)', 'mod_c(a, b)', n=n))
print(s.format(*(('-' * (n - 1),) * 4), n=n))
for a, b in itertools.product(vals, repeat=2):
print(s.format(a, b, mod(a, b), mod_c0(a, b), n=n))
a b mod(a, b) mod_c(a, b)
------------- ------------- ------------- -------------
3 3 0 0
3 -3 0 0
3 31 3 3
3 -31 -28 3
-3 3 0 0
-3 -3 0 0
-3 31 28 -3
-3 -31 -3 -3
31 3 1 1
31 -3 -2 1
31 31 0 0
31 -31 0 0
-31 3 2 -1
-31 -3 -1 -1
-31 31 0 0
-31 -31 0 0
更多测试和基准测试:
n = 100
k = 1
l = [x for x in range(-n, n + k, k)]
ll = [(a, b) for a, b in itertools.product(l, repeat=2) if b]
funcs = mod_c0, mod_c1, mod_c2, mod_c3, mod_c4, mod_c5, mod_c6, mod_c7, mod_c8, mod_c9
for func in funcs:
correct = all(func(a, b) == funcs[0](a, b) for a, b in ll)
print(func.__name__, 'correct:', all_equal)
%timeit [func(a, b) for a, b in ll]
print()
mod_c0 correct: True
100 loops, best of 3: 6.6 ms per loop
mod_c1 correct: True
100 loops, best of 3: 7.86 ms per loop
mod_c2 correct: True
100 loops, best of 3: 8.49 ms per loop
mod_c3 correct: True
100 loops, best of 3: 7.56 ms per loop
mod_c4 correct: True
100 loops, best of 3: 7.5 ms per loop
mod_c5 correct: True
100 loops, best of 3: 7.94 ms per loop
mod_c6 correct: True
100 loops, best of 3: 13.4 ms per loop
mod_c7 correct: True
100 loops, best of 3: 16.8 ms per loop
mod_c8 correct: True
100 loops, best of 3: 12.4 ms per loop
mod_c9 correct: True
100 loops, best of 3: 6.48 ms per loop
也许有更好(更短?、更快?)的方法,因为使用 C 的 %
实现 Python 的 %
似乎更简单:
((a % b) + b) % b
了解 C 风格的 %
计算(上面的 mod_c*()
函数)如何与通常的 %
或获得 [=90= 所需的操作相对立] 风格 %
来自 C
:
def mod_py(a, b):
return a % b
def mod_c2py(a, b):
return ((a % b) + b) % b
%timeit [mod_py(a, b) for a, b in ll]
# 100 loops, best of 3: 5.85 ms per loop
%timeit [mod_c2py(a, b) for a, b in ll]
# 100 loops, best of 3: 7.84 ms per loop
当然请注意,mod_c2py()
仅用于了解我们可以从 mod_c()
函数中获得什么样的性能。
(已编辑 以修复一些建议的方法并包括一些时间安排)
(EDITED-2 添加 mod_c5()
解决方案)
(EDITED-3 将 mod_c6()
添加到 mod_c9()
解决方案)
对于 64 位整数,这些都应该有效:
def mod_c_AA0(a,b):
x=(a^b)>>63
return a % (b*(x|1))
def mod_c_AA1(a,b):
x=(a^b)>>63
return a % ((-b & x)|(b & ~x))
使用二进制补码。正如 norok2 所建议的那样,根据 a
和 b
.
a_xor_b=a^b; x=a_xor_b>>a_xor_b.bit_length();
以获得位移的最佳特异性
我正在跟进@norok2 的非常全面的回答。 我已经尝试过使用分支的超级天真方法,它似乎略有但始终更快 (~2-4%)。
def mod_naive(x,y):
if y < 0:
if x < 0:
return x%y
else:
return (x%-y)
else:
if x < 0:
return -(-x%y)
else:
return x%y
或使用 lambda(不影响速度,仅影响凉爽):
mod_naive = lambda x,y: (x%y if x < 0 else x%-y) if y < 0 else (-(-x%y) if x < 0 else x%y)
与@norok2 最快的解决方案 (mod_c0
) 相比:
mod_c0 correct: True 100 loops, best of 3: 6.86 ms per loop mod_naive correct: True 100 loops, best of 3: 6.58 ms per loop
我(天真的)猜测分支预测算法最终会产生较少的整体操作。