以下cython脚本会导致comparison between signed and unsigned integer expressions
警告。
%%cython
# distutils: language = c++
from libcpp.vector cimport vector as cpp_vec
import numpy as np
cdef cpp_vec[long] a = np.array([1,2,3], np.int)
cdef Py_ssize_t i
for i in range(a.size()): # Using a.size() as parameter of range() causes the warning
pass
此警告是否必要?如果是这样,为什么?此外,是否可以使这些未签名和已签名的比较警告静音?
还有,为什么只有第一个for
循环会导致警告?
%%cython
cdef:
ssize_t i
unsigned long m = 10
unsigned int n = 10
long o = 10
for i in range(m):
pass
for i in range(n):
pass
for i in range(o):
pass
答案 0 :(得分:1)
这是您的g ++编译器发出的警告,由于每次编译器警告都应得到认真对待。
您的Cython代码的for循环转换为以下/类似的cpp代码:
std::vector<long>::size_type __pyx_t_7;
Py_ssize_t __pyx_t_8;
__pyx_t_7 = __pyx_v_xxxx_a.size();
for (__pyx_t_8 = 0; __pyx_t_8 < __pyx_t_7; __pyx_t_8+=1) {
....
}
问题在于,std::vector<long>::size_type
是64位系统上的64位无符号整数,而Py_ssize_t
是64位系统上的有符号整数。
C ++对内置类型使用隐式转换,以便能够评估__pyx_t_8 < __pyx_t_7
,例如可以在此SO-post中找到规则。
这个警告有意义的原因有很多-规则非常复杂,经验表明,程序员经常会错误地处理它们。例如,-1<1U
的计算结果为false
(请参阅live),但
signed char a =-1;
unsigned char b = 1;
std::cout<<(a<b)<<"\n";
打印1
,即(a<b)
的值为true
(请参阅live)。这样的怪癖很容易导致难以发现的错误。
从历史上看,甚至在建立标准之前,不同的编译器以不同的方式处理这些转换-当切换到不同的编译器时,很高兴看到可以更改行为的所有位置-但这并不是很重要如今。
有多种避免这种警告的策略。
1。顺其自然:
将i
设为size_t
而不是Py_ssize_t
(即cdef size_t i
)不是更容易吗?
2。投放并检查:
您可以明确地进行强制转换,然后检查这些假设是否正确,例如
...
cdef Py_ssize_t i
cdef Py_ssize_t n = <Py_ssize_t>(a.size())
if n<0 :
raise ValueError("too big");
for i in range(n):
...
以上内容变得非常麻烦,因此通常将其提取到实用程序功能中。
3。刚投放:
人们可能会问:“一个向量有多于2^63
个条目的可能性是多少?”然后跳过检查:
...
for i in range(<Py_ssize_t>(a.size())):
print(i)
然后... 30年后看到代码失败:)
4。禁用编译器警告:
还可以通过安装文件中的-Wno-sign-compare
或IPython中的extra_compile_args
将-c=-Wno-sign-compare
-选项传递给编译器,这将关闭整个代码中的警告。使用“仅强制转换”解决方案可能更安全,该解决方案仅在此位置而不是在所有位置使警告静音。
如果有符号整数的大小大于无符号整数的大小,则不会发出警告:在这种情况下,无符号整数将转换为较大的有符号整数,并适合较大类型的正数范围。
例如ssize_t
具有64位,而unsigned int
具有32位,因此在比较之前32位无符号位被转换为64位-ssize_t
-不会溢出,因为正数最多到63可以用`ssize_t'表示。