463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 123450

Assign negative of unsigned to a signed, is it OK?

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

Upvotes: 5

Views: 298

Answers (2)

user2296177
user2296177

Reputation: 2837

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

Upvotes: 5

keith
keith

Reputation: 5352

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.

Upvotes: 0

Related Questions