Cython 发出 'unnecessary' 警告
Cython giving 'unnecessary' warning
以下 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
这是来自您的 g++ 编译器的警告,每个 compiler-warning 都应该认真对待。
您的 Cython 代码的 for-loop 被转换为 following/similar cpp-code:
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++ 对 built-in 类型使用隐式转换,以便能够计算 __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)。这种怪癖很容易导致 hard-to-find 错误。
更重要的是,从历史上看,在标准建立之前,不同的编译器以不同的方式处理这些转换 - 当切换到不同的编译器时,很高兴看到所有可以改变行为的地方 - 但这不是现在真的很重要。
有不同的策略可以避免此警告。
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):
...
以上变得很快,相当繁琐,所以通常将其提取到utility-function。
3。刚投:
可以问 "how probable it is that a vector has more than 2^63
entries?" 并跳过检查:
...
for i in range(<Py_ssize_t>(a.size())):
print(i)
然后...看到代码在 30 年后失败了:)
4.禁用编译器警告:
也可以通过 setup-file 中的 extra_compile_args
或 IPython 中的 -c=-Wno-sign-compare
将 -Wno-sign-compare
选项传递给编译器,这将关闭警告在整个代码中。使用 "Just cast" 解决方案可能更安全,它只在这个地方而不是所有地方使警告静音。
如果有符号整数的大小大于无符号整数,则不会发出警告:在这种情况下,无符号整数将转换为更大的有符号整数并适合较大类型的正数范围。
例如ssize_t
有64位而unsigned int
有32位,因此32位无符号位在比较之前转换为64位-ssize_t
- 不会溢出,因为正数最多 63 的数字可以用“ssize_t”表示。
以下 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
这是来自您的 g++ 编译器的警告,每个 compiler-warning 都应该认真对待。
您的 Cython 代码的 for-loop 被转换为 following/similar cpp-code:
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++ 对 built-in 类型使用隐式转换,以便能够计算 __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)。这种怪癖很容易导致 hard-to-find 错误。
更重要的是,从历史上看,在标准建立之前,不同的编译器以不同的方式处理这些转换 - 当切换到不同的编译器时,很高兴看到所有可以改变行为的地方 - 但这不是现在真的很重要。
有不同的策略可以避免此警告。
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):
...
以上变得很快,相当繁琐,所以通常将其提取到utility-function。
3。刚投:
可以问 "how probable it is that a vector has more than 2^63
entries?" 并跳过检查:
...
for i in range(<Py_ssize_t>(a.size())):
print(i)
然后...看到代码在 30 年后失败了:)
4.禁用编译器警告:
也可以通过 setup-file 中的 extra_compile_args
或 IPython 中的 -c=-Wno-sign-compare
将 -Wno-sign-compare
选项传递给编译器,这将关闭警告在整个代码中。使用 "Just cast" 解决方案可能更安全,它只在这个地方而不是所有地方使警告静音。
如果有符号整数的大小大于无符号整数,则不会发出警告:在这种情况下,无符号整数将转换为更大的有符号整数并适合较大类型的正数范围。
例如ssize_t
有64位而unsigned int
有32位,因此32位无符号位在比较之前转换为64位-ssize_t
- 不会溢出,因为正数最多 63 的数字可以用“ssize_t”表示。