This is a warning from your g++-compiler, and as every compiler-warning should be taken seriously.
The for-loop of your Cython code is transformed to the 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) {
....
}
The problem is, that std::vector<long>::size_type
is 64bit unsigned integer ona 64-bit system, and Py_ssize_t
a 64bit signed integer.
C++ uses implicit conversion for built-in types, in order to be able to evaluate __pyx_t_8 < __pyx_t_7
, the rules can be found for example in this SO-post.
There are multiple reason, why this warning makes sense - the rules are quite complicated and experience has shown, that programmers often handle them wrong. For example -1<1U
evaluates to false
(see live), but
signed char a =-1;
unsigned char b = 1;
std::cout<<(a<b)<<"\n";
prints 1
, i.e. (a<b)
evaluates to true
(see live). Such quirks can easily leads to hard-to-find bugs.
Even more, historically, before the standard was established, different compilers handled those conversions differently - when switching to a different compiler, it was a nice thing to see all the places where the behavior could be changed - but this not really important nowdays.
There are different strategies to avoid this warning.
1. Go with the flow:
Isn't easier to make the i
a size_t
instead of Py_ssize_t
, i.e cdef size_t i
?
2. Cast and check:
You can explicitly cast and then check, whether the assumptions are OK, e.g.
...
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):
...
The above becomes fast quite cumbersome, so one usually extract this into a utility-function.
3. Just cast:
One could ask "how probable it is that a vector has more than 2^63
entries?" and just skip the checking:
...
for i in range(<Py_ssize_t>(a.size())):
print(i)
and then... see the code fail 30 years later:)
4. Disable compiler warning:
One also could pass -Wno-sign-compare
-option to the compiler via extra_compile_args
in setup-file or -c=-Wno-sign-compare
in IPython, which would turn off the warning in the whole code. It is probably safer to use the "Just cast"-solution, which mutes the warning only at this one place and not everywhere.
The warning is not issued if signed integer is of larger size than the unsigned integer: In this case the unsigned integer is converted to larger signed integer and fits into positive range of the larger type.
For example ssize_t
has 64bit and unsigned int
has 32bit, thus 32bit-unsigned bit is converted to 64bit-ssize_t
before the comparison - the will be no overflow, because positive numbers up to 63 can be represented by `ssize_t´.