Cython发出“不必要的”警告

时间:2019-05-10 16:07:53

标签: python warnings cython unsigned

以下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

1 个答案:

答案 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'表示。