2

This code gives the meaningful output

#include <iostream>

int main() {
    unsigned int ui = 100;
    unsigned int negative_ui = -22u;

    std::cout << ui + negative_ui << std::endl;

}

Output:

78

The variable negative_ui stores -22, but is an unsigned int. My question is why does unsigned int negative_ui = -22u; work. How can an unsigned int store a negative number? Is it save to be used or does this yield undefined behaviour?

I use the intel compiler 18.0.3. With the option -Wall no warnings occurred.

Ps. I have read What happens if I assign a negative value to an unsigned variable? and Why unsigned int contained negative number

schorsch312
  • 5,553
  • 5
  • 28
  • 57
  • does not work with MSVC 2019: error C4146: unary minus operator applied to unsigned type, result still unsigned – P. PICARD May 21 '19 at 11:21
  • It's not safe and it's useless. Don't waste your time with stuff like this. – Jabberwocky May 21 '19 at 11:21
  • 1
    Please explain how [this](https://stackoverflow.com/a/2711560/4117728) does not already answer your first question. for your question 2 and 3: `unsigned` cannot store negative numbers and what do you mean with "save" ? – 463035818_is_not_an_ai May 21 '19 at 11:24
  • iirc, the parser sees: `-` as a unary minus, and `22u` as an unsigned. So it is "unary-minus operation on twenty-two". It is _not_ parsed as `-22u` (negative twenty-two). I'd have to experiment to see the ramifications. – Eljay May 21 '19 at 11:37
  • `22u` has the type `unsigned int`, as expected. `-22u` also has the type `unsigned int`, possibly counterintuitive. The upshot is `-22u` is not a negative number. – Eljay May 21 '19 at 11:48

2 Answers2

9

How can an unsigned int store a negative number?

It doesn't. Instead, it stores a representable number that is congruent with that negative number modulo the number of all representable values. The same is also true with results that are larger than the largest representable value.

Is it save to be used or does this yield undefined behaviour?

There is no UB. Unsigned arithmetic overflow is well defined.

It is safe to rely on the result. However, it can be brittle. For example, if you add -22u and 100ull, then you get UINT_MAX + 79 (i.e. a large value assuming unsigned long long is a larger type than unsigned) which is congruent with 78 modulo UINT_MAX + 1 that is representable in unsigned long long but not representable in unsigned.

Note that signed arithmetic overflow is undefined.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

Signed/Unsigned is a convention. It uses the last bit of the variable (in case of x86 int, the last 31th bit). What you store in the variable takes the full bit length.

It's the calculations that follow that take the upper bit as a sign indicator or ignore it. Therefore, any "unsigned" variable can contain a signed value which will be converted to the unsigned form when the unsigned variable participates in a calculation.

unsigned int x = -1; // x is now 0xFFFFFFFF.  
x -= 1; //  x is now 0xFFFFFFFE. 
if (x < 0) // false. x is compared as 0xFFFFFFFE.

int x = -1; // x stored as 0xFFFFFFFF
x -= 1; // x stored as 0xFFFFFFFE
if (x < 0) // true, x is compared as -2.

Technically valid, bad programming.

Michael Chourdakis
  • 10,345
  • 3
  • 42
  • 78
  • 6
    you are mixing what the hardware does with what the c++ standard defines. In standard c++ signed / unsigned is not merely a convention, but those are two distinct types. The hardware doing exactly the same for both types is a different story – 463035818_is_not_an_ai May 21 '19 at 11:26
  • He's asking why he is able to assign an unsigned to a signed variable. C++ will allow it despite having distinct types, so I'm trying to explain why. Different or not different story, it's what's happening, so he has to understand it. – Michael Chourdakis May 21 '19 at 11:27
  • 3
    yes, and my critic is: what is allowed and what works is rather independent of what the hardware does or how the values are actually represented in memory. As an example take overflow. It is well defined for unsigned but ub for signed integer, even if for the hardware there is no difference whatsoever – 463035818_is_not_an_ai May 21 '19 at 11:29