4

When I run this:

int main() {
    unsigned a = 5;
    std::cout << -a << std::endl;
    int b = -a; 
    std::cout << b << std::endl;
    return 0;
}

I get this:

4294967291
-5

It seems like it works, and I can take the negative of an unsigned and assign it to an int, but is this really always OK? Why?

When I try something that to me looks like a similar situation:

int c = 1;
int d = 3;
double x = c/d;
std::cout << x << std::endl;

I get 0 (as expected).

PS: Maybe there is a dupe and I didnt find it, closest I could find is this

Community
  • 1
  • 1
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • I am "almost" sure C++ inherits this type conversion from C - consider to add C tag and search C area.. E.g. read this http://stackoverflow.com/questions/50605/signed-to-unsigned-conversion-in-c-is-it-always-safe. – PiotrNycz Aug 31 '16 at 14:31
  • It is just a binary magic. The same binary representation is interpreted as a signed or unsigned value differently. – ilotXXI Aug 31 '16 at 14:33
  • see also here http://stackoverflow.com/questions/7221409/is-unsigned-integer-subtraction-defined-behavior – Hayt Aug 31 '16 at 14:34
  • @JonnyHenly with "always OK" I meant that I wasnt sure if I just hit a lucky example, because actually I didnt expect this to work. The second example is because the only way I could explain the behaviour is that the rhs is somehow promoted to signed (ie. the type on the lhs) before the assignment – 463035818_is_not_an_ai Aug 31 '16 at 14:37

2 Answers2

4

No. You have undefined behaviour possibilities.

Here is a counter-example that produces UB when assigning a negated unsigned int to an int:

unsigned u = (unsigned)std::numeric_limits<int>::max() - 1;
std::cout << "max int" << std::numeric_limits<int>::max() << '\n';
std::cout << "as unsigned - 1" << u << '\n';
std::cout << "negated:" << -u << '\n';
std::cout << std::boolalpha << ( std::numeric_limits<int>::max() < -u ) << '\n';
int s = -u;
std::cout << s << '\n';

On my machine: int's max value is 2'147'483'647, but the negated unsigned int has a value of 2'147'483'650; that value is greater than the max value that can be represented by an int. Know that signed overflow is undefined behaviour. Thus, the algorithm is not safe for all of its possible values.

The Standard's (2016-07-12: N4604) word:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined. [ Note: Treatment of division by zero, forming a remainder using a zero divisor, and all floating point exceptions vary among machines, and is sometimes adjustable by a library function. — end note ]


In the future, you can use the {}-style initialization to prevent such issues:

unsigned a = 5;
std::cout << -a << '\n';
int b{ -a }; // compiler detects narrowing conversions, warning/error
std::cout << b << '\n';
return 0;

Note that even though you know that -a will be a value that can be represented by an int, your compiler still warns you.

On signed overflow:

Is signed integer overflow still undefined behavior in C++?

On well defined unsigned overflow in both C and C++:

Why is unsigned integer overflow defined behavior but signed integer overflow isn't?

On implicit conversions:

http://en.cppreference.com/w/cpp/language/implicit_conversion

Community
  • 1
  • 1
user2296177
  • 2,807
  • 1
  • 15
  • 26
  • 2
    @cmaster If you assign a value that is larger than what can be represented by the `int`, you trigger UB. Even if the original operation on the `unsigned` is well defined. – user2296177 Aug 31 '16 at 14:57
  • 2
    @cmaster I have not seen that. C++14 still has [expr]/4 *If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.* – NathanOliver Aug 31 '16 at 15:05
  • @cmaster I've added a quote from a recent C++ standard that contradicts what you say. Remember that this question is C++, and you might be correct in C. – user2296177 Aug 31 '16 at 15:08
  • Ok, probably you are right for C++. Deleted my stuff. – cmaster - reinstate monica Aug 31 '16 at 15:14
0

It is OK just as long as your target architecture is using two's compliment arithmetic and is treating int as 32 bits. Otherwise you'll get different results for your first program.

keith
  • 5,122
  • 3
  • 21
  • 50