Comparison between signed and unsigned integer types seems to be a simple case, 
but sometimes it leads to nasty bugs in the code.

Briefly recall the problem:
Each integer type with opposite signed and unsigned identificators occupies the 
same size in the memory.
But being unsigned, it can only carry non-negative numbers. On the other hand, 
being signed the integer type represents both signed and unsigned numbers.

The signed integer type uses 1 bit from number representation as the sign 
detector (0 - positive, 1 - negative).
It means that, given the fact that the signed and unsigned occupy the same 
amount of memory, the range of values ​​will be different.

For example, if the integer has 32 bits size in the system:
- signed int value range is (+-)2^31 (from -2,147,483,648 to 2,147,483,647)
- unsigned int value range is 2^32 (from 0 to 4,294,967,295)

Another important thing is the signed type uses the Two's complement method for 
numbers representation.
The method does following steps:
- It reserves the first bit for distinguishing negative and positive numbers
- It inverts the rest of bits and add 1.

For example, after applying the Two's complement method, 'signed int a = -1' is 
represented as '111111111’111111111’111111111’111111111' (32 bit integer) .
But the unsigned type doesn't use such a method. In case of unsigned int, the 
'111111111’111111111’111111111’111111111' is just the biggest possible value 
for 32-bit integer (4,294,967,295).

As a result, the same number representation ​​may be interpreted differently.

There is a way to detect such problem by using "-werror/-wsign-compare" 
compiler options.
Options find all places that execute such comparisons and interpret them like 
errors.
But fixing comparison errors always was on a developer.

Nowadays C++20 introduces a safe way to compare signed and unsigned integer 
types.
A new API description can be found here: 
https://en.cppreference.com/w/cpp/utility/intcmp 
<https://en.cppreference.com/w/cpp/utility/intcmp>
The solution treats all negative numbers as smaller then any non-negative.
The nice thing about the new API it is simple and can be easily ported to the 
older C++ versions.
So we introduced the backport as a part of a Qt 'q20' namespace.
The implementation can be found here: 
https://codereview.qt-project.org/c/qt/qtbase/+/570370/29/src/corelib/global/q20utility.h

The backport gives to Qt users a possibility to start using the safe comparison 
with C++17 and since Qt 6.9.
The 'q20' namespace checks if std implementation is available and takes it, 
otherwise it takes our implementation.

How to use it: https://codereview.qt-project.org/c/qt/qtbase/+/578720
In short, I hope it can be useful for development. And let's use the API (when 
it's suitable).

Also, I am thinking about better detection methods. A clang-tidy tool is a 
potential candidate.

Best regards,
Tatiana
-- 
Development mailing list
Development@qt-project.org
https://lists.qt-project.org/listinfo/development

Reply via email to