c++ safeness of code with implicit conversion between signed and unsigned

1497 views c++
10

According to the rules on implicit conversions between signed and unsigned integer types, discussed here and here, when summing an unsigned int with a int, the signed int is first converted to an unsigned int.

Consider, e.g., the following minimal program

#include <iostream>

int main()
{
   unsigned int n = 2;
   int x = -1;

   std::cout << n + x << std::endl;

   return 0;
}

The output of the program is, nevertheless, 1 as expected: x is converted first to an unsigned int, and the sum with n leads to an integer overflow, giving the "right" answer.

In a code like the previous one, if I know for sure that n + x is positive, can I assume that the sum of unsigned int n and int x gives the expected value?

answered question

Are you compiling with all warnings enabled?

@JGroven how will that impact the answer?

@Fureeish, if the compiler warns you about what you're doing, you probably shouldn't be doing it, which would mean that the assumption that the expected value is correct would be false.

Well, probably, would and so on are not guarantees. The fact that compiler may warn about possible problems does not mean that, given a specific criteria, like "if I know for sure that n + x is positive", it will, or even it could fail

@JGroven I know that the compiler issues a warning when Wsign-conversion (gcc or clang) is enabled. Still I would like to understand if this warning is a serious issue or not.

This would depend I think on the standard guaranteeing the conversion to unsigned to be in 2's complement format which kind of rings a bell...

@JGroven "if the compiler warns you about what you're doing, you probably shouldn't be doing it." Err it's a "warning" for a reason--to bring it to your attention, just in case. There are many cases I've run into where I have WARNING: Unused variable X in production code that are actually fine (maybe because the declaration alone had an effect or the compiler is just wrong).

@Asu I am aware of the discussion you link and in fact I have mentioned and linked it in the question.

This will be implementation defined. The standard says the bit patterns for signed and unsigned will be identical but 2's complement is not guaranteed. So it would be implementation defined if this would work or not. I believe that std::int8/16/32_t are guaranteed to be 2's complement so they would always work.

1 Answer

0

One way to check your assumption for this case is to use UBSan unsigned integer overflow detection using -fsanitize=integer and we can see from a live example that we do indeed overflow:

prog.cc:8:21: runtime error: implicit conversion from type 'int' of value -1 (32-bit, signed) to type 'unsigned int' changed the value to 4294967295 (32-bit, unsigned)
prog.cc:8:19: runtime error: unsigned integer overflow: 2 + 4294967295 cannot be represented in type 'unsigned int'

This is not an inuitive result and we should avoid these type of interactions exactly because they lead to hard to reason about results. In general I would consider that an error and avoid mixing signed and unsigned types like when doing math.

Using UBSan and other tools such as -Wconversion to detect and avoid these issues.

posted this

Have an answer?

JD

Please login first before posting an answer.