Why is the sign different after substracting unsigned and signed?
Clash Royale CLAN TAG#URR8PPP
up vote
8
down vote
favorite
unsigned int t = 10;
int d = 16;
float c = t - d;
int e = t - d;
Why is the value of c
positive but e
negative?
c++
New contributor
Eugene Kolombet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |Â
up vote
8
down vote
favorite
unsigned int t = 10;
int d = 16;
float c = t - d;
int e = t - d;
Why is the value of c
positive but e
negative?
c++
New contributor
Eugene Kolombet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |Â
up vote
8
down vote
favorite
up vote
8
down vote
favorite
unsigned int t = 10;
int d = 16;
float c = t - d;
int e = t - d;
Why is the value of c
positive but e
negative?
c++
New contributor
Eugene Kolombet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
unsigned int t = 10;
int d = 16;
float c = t - d;
int e = t - d;
Why is the value of c
positive but e
negative?
c++
c++
New contributor
Eugene Kolombet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Eugene Kolombet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
edited 3 mins ago
Angew
128k11237333
128k11237333
New contributor
Eugene Kolombet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
asked 31 mins ago


Eugene Kolombet
411
411
New contributor
Eugene Kolombet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Eugene Kolombet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
Eugene Kolombet is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |Â
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
13
down vote
Let's start by analysing the result of t - d
.
t
is an unsigned int
while d
is an int
, so to do arithmetic on them, the value of d
is converted to an unsigned int
(C++ rules say unsigned gets preference here). So we get 10u - 16u
, which (assuming 32-bit int
) wraps around to 4294967290u
.
This value is then converted to float
in the first declaration, and to int
in the second one.
Assuming the typical implementation of float
(32-bit single-precision IEEE), its highest representable value is roughly 1e38
, so 4294967290u
is well within that range. There will be rounding errors, but it's representable.
For int
, the situation's different. 4294967290u
is too big to fit into an int
, so wrap-around happens and we arrive back at the value -6
. Note that such wrap-around is not guaranteed by the standard: the behaviour in this case is implementation-defined(1), which means it's up to the compiler how it's handled, but it must be documented.
(1) C++17 (N4659), [conv.integral] 7.8/3:
If the destination type is signed, the value is unchanged if it can be represented in the destination type;
otherwise, the value is implementation-defined.
Just curious, but does in this case implementation-defined means hardware related? Since, most common hardware is two's-complement, you got this behavior, but it would change on a ones-complement CPU even using the same compiler?
– user1810087
9 mins ago
1
@user1810087 It means that the compiler must document what will happen, which means the compiler authors must think about what will happen, and implement it consistently (or at least document it consistently with implementation). It's quite likely their decision will be influenced by the target HW, and so the documentation of this will be platform-specific.
– Angew
4 mins ago
add a comment |Â
up vote
5
down vote
First, you have to understand "usual arithmetic conversions" (that link is for C, but the rules are the same in C++). In C++, if you do arithmetic with mixed types (you should avoid that when possible, by the way), there's a set of rules that decides which type the calculation is done in.
In your case, you are subtracting a signed int from an unsigned int. The promotion rules say that the actual calculation is done using unsigned int
.
So your calculation is 10 - 16
in unsigned int arithmetic. Unsigned arithmetic is modulo arithmetic, meaning that it wraps around. So, assuming your typical 32-bit int, the result of this calculation is 2^32 - 6.
This is the same for both lines. Note that the subtraction is completely independent from the assignment; the type on the left side has absolutely no influence on how the calculation happens. It is a common beginner mistake to think that the type on the left side somehow influences the calculation; but float f = 5 / 6
is zero, because the division still uses integer arithmetic.
The difference, then, is what happens during the assignment. The result of the subtraction is implicitly converted to float
in one case, and int
in the other.
The conversion to float tries to find the closest value to the actual one that the type can represent. This will be some very large value; not quite the one the original subtraction yielded though.
The conversion to int says that if the value fits into the range of int, the value will be unchanged. But 2^32 - 6 is far larger than the 2^32 - 1 that a 32-bit int can hold, so you get the other part of the conversion rule, which says that the resulting value is implementation-defined. This is a term in the standard that means "different compilers can do different things, but they have to document what they do".
For all practical purposes, all compilers that you'll likely encounter say that the bit pattern stays the same and is just interpreted as signed. Because of the way 2's complement arithmetic works (the way that almost all computers represent negative numbers), the result is the -6 you would expect from the calculation.
But all this is a very long way of repeating the first point, which is "don't do mixed type arithmetic". Cast the types first, explicitly, to types that you know will do the right thing.
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
13
down vote
Let's start by analysing the result of t - d
.
t
is an unsigned int
while d
is an int
, so to do arithmetic on them, the value of d
is converted to an unsigned int
(C++ rules say unsigned gets preference here). So we get 10u - 16u
, which (assuming 32-bit int
) wraps around to 4294967290u
.
This value is then converted to float
in the first declaration, and to int
in the second one.
Assuming the typical implementation of float
(32-bit single-precision IEEE), its highest representable value is roughly 1e38
, so 4294967290u
is well within that range. There will be rounding errors, but it's representable.
For int
, the situation's different. 4294967290u
is too big to fit into an int
, so wrap-around happens and we arrive back at the value -6
. Note that such wrap-around is not guaranteed by the standard: the behaviour in this case is implementation-defined(1), which means it's up to the compiler how it's handled, but it must be documented.
(1) C++17 (N4659), [conv.integral] 7.8/3:
If the destination type is signed, the value is unchanged if it can be represented in the destination type;
otherwise, the value is implementation-defined.
Just curious, but does in this case implementation-defined means hardware related? Since, most common hardware is two's-complement, you got this behavior, but it would change on a ones-complement CPU even using the same compiler?
– user1810087
9 mins ago
1
@user1810087 It means that the compiler must document what will happen, which means the compiler authors must think about what will happen, and implement it consistently (or at least document it consistently with implementation). It's quite likely their decision will be influenced by the target HW, and so the documentation of this will be platform-specific.
– Angew
4 mins ago
add a comment |Â
up vote
13
down vote
Let's start by analysing the result of t - d
.
t
is an unsigned int
while d
is an int
, so to do arithmetic on them, the value of d
is converted to an unsigned int
(C++ rules say unsigned gets preference here). So we get 10u - 16u
, which (assuming 32-bit int
) wraps around to 4294967290u
.
This value is then converted to float
in the first declaration, and to int
in the second one.
Assuming the typical implementation of float
(32-bit single-precision IEEE), its highest representable value is roughly 1e38
, so 4294967290u
is well within that range. There will be rounding errors, but it's representable.
For int
, the situation's different. 4294967290u
is too big to fit into an int
, so wrap-around happens and we arrive back at the value -6
. Note that such wrap-around is not guaranteed by the standard: the behaviour in this case is implementation-defined(1), which means it's up to the compiler how it's handled, but it must be documented.
(1) C++17 (N4659), [conv.integral] 7.8/3:
If the destination type is signed, the value is unchanged if it can be represented in the destination type;
otherwise, the value is implementation-defined.
Just curious, but does in this case implementation-defined means hardware related? Since, most common hardware is two's-complement, you got this behavior, but it would change on a ones-complement CPU even using the same compiler?
– user1810087
9 mins ago
1
@user1810087 It means that the compiler must document what will happen, which means the compiler authors must think about what will happen, and implement it consistently (or at least document it consistently with implementation). It's quite likely their decision will be influenced by the target HW, and so the documentation of this will be platform-specific.
– Angew
4 mins ago
add a comment |Â
up vote
13
down vote
up vote
13
down vote
Let's start by analysing the result of t - d
.
t
is an unsigned int
while d
is an int
, so to do arithmetic on them, the value of d
is converted to an unsigned int
(C++ rules say unsigned gets preference here). So we get 10u - 16u
, which (assuming 32-bit int
) wraps around to 4294967290u
.
This value is then converted to float
in the first declaration, and to int
in the second one.
Assuming the typical implementation of float
(32-bit single-precision IEEE), its highest representable value is roughly 1e38
, so 4294967290u
is well within that range. There will be rounding errors, but it's representable.
For int
, the situation's different. 4294967290u
is too big to fit into an int
, so wrap-around happens and we arrive back at the value -6
. Note that such wrap-around is not guaranteed by the standard: the behaviour in this case is implementation-defined(1), which means it's up to the compiler how it's handled, but it must be documented.
(1) C++17 (N4659), [conv.integral] 7.8/3:
If the destination type is signed, the value is unchanged if it can be represented in the destination type;
otherwise, the value is implementation-defined.
Let's start by analysing the result of t - d
.
t
is an unsigned int
while d
is an int
, so to do arithmetic on them, the value of d
is converted to an unsigned int
(C++ rules say unsigned gets preference here). So we get 10u - 16u
, which (assuming 32-bit int
) wraps around to 4294967290u
.
This value is then converted to float
in the first declaration, and to int
in the second one.
Assuming the typical implementation of float
(32-bit single-precision IEEE), its highest representable value is roughly 1e38
, so 4294967290u
is well within that range. There will be rounding errors, but it's representable.
For int
, the situation's different. 4294967290u
is too big to fit into an int
, so wrap-around happens and we arrive back at the value -6
. Note that such wrap-around is not guaranteed by the standard: the behaviour in this case is implementation-defined(1), which means it's up to the compiler how it's handled, but it must be documented.
(1) C++17 (N4659), [conv.integral] 7.8/3:
If the destination type is signed, the value is unchanged if it can be represented in the destination type;
otherwise, the value is implementation-defined.
edited 16 mins ago
answered 23 mins ago
Angew
128k11237333
128k11237333
Just curious, but does in this case implementation-defined means hardware related? Since, most common hardware is two's-complement, you got this behavior, but it would change on a ones-complement CPU even using the same compiler?
– user1810087
9 mins ago
1
@user1810087 It means that the compiler must document what will happen, which means the compiler authors must think about what will happen, and implement it consistently (or at least document it consistently with implementation). It's quite likely their decision will be influenced by the target HW, and so the documentation of this will be platform-specific.
– Angew
4 mins ago
add a comment |Â
Just curious, but does in this case implementation-defined means hardware related? Since, most common hardware is two's-complement, you got this behavior, but it would change on a ones-complement CPU even using the same compiler?
– user1810087
9 mins ago
1
@user1810087 It means that the compiler must document what will happen, which means the compiler authors must think about what will happen, and implement it consistently (or at least document it consistently with implementation). It's quite likely their decision will be influenced by the target HW, and so the documentation of this will be platform-specific.
– Angew
4 mins ago
Just curious, but does in this case implementation-defined means hardware related? Since, most common hardware is two's-complement, you got this behavior, but it would change on a ones-complement CPU even using the same compiler?
– user1810087
9 mins ago
Just curious, but does in this case implementation-defined means hardware related? Since, most common hardware is two's-complement, you got this behavior, but it would change on a ones-complement CPU even using the same compiler?
– user1810087
9 mins ago
1
1
@user1810087 It means that the compiler must document what will happen, which means the compiler authors must think about what will happen, and implement it consistently (or at least document it consistently with implementation). It's quite likely their decision will be influenced by the target HW, and so the documentation of this will be platform-specific.
– Angew
4 mins ago
@user1810087 It means that the compiler must document what will happen, which means the compiler authors must think about what will happen, and implement it consistently (or at least document it consistently with implementation). It's quite likely their decision will be influenced by the target HW, and so the documentation of this will be platform-specific.
– Angew
4 mins ago
add a comment |Â
up vote
5
down vote
First, you have to understand "usual arithmetic conversions" (that link is for C, but the rules are the same in C++). In C++, if you do arithmetic with mixed types (you should avoid that when possible, by the way), there's a set of rules that decides which type the calculation is done in.
In your case, you are subtracting a signed int from an unsigned int. The promotion rules say that the actual calculation is done using unsigned int
.
So your calculation is 10 - 16
in unsigned int arithmetic. Unsigned arithmetic is modulo arithmetic, meaning that it wraps around. So, assuming your typical 32-bit int, the result of this calculation is 2^32 - 6.
This is the same for both lines. Note that the subtraction is completely independent from the assignment; the type on the left side has absolutely no influence on how the calculation happens. It is a common beginner mistake to think that the type on the left side somehow influences the calculation; but float f = 5 / 6
is zero, because the division still uses integer arithmetic.
The difference, then, is what happens during the assignment. The result of the subtraction is implicitly converted to float
in one case, and int
in the other.
The conversion to float tries to find the closest value to the actual one that the type can represent. This will be some very large value; not quite the one the original subtraction yielded though.
The conversion to int says that if the value fits into the range of int, the value will be unchanged. But 2^32 - 6 is far larger than the 2^32 - 1 that a 32-bit int can hold, so you get the other part of the conversion rule, which says that the resulting value is implementation-defined. This is a term in the standard that means "different compilers can do different things, but they have to document what they do".
For all practical purposes, all compilers that you'll likely encounter say that the bit pattern stays the same and is just interpreted as signed. Because of the way 2's complement arithmetic works (the way that almost all computers represent negative numbers), the result is the -6 you would expect from the calculation.
But all this is a very long way of repeating the first point, which is "don't do mixed type arithmetic". Cast the types first, explicitly, to types that you know will do the right thing.
add a comment |Â
up vote
5
down vote
First, you have to understand "usual arithmetic conversions" (that link is for C, but the rules are the same in C++). In C++, if you do arithmetic with mixed types (you should avoid that when possible, by the way), there's a set of rules that decides which type the calculation is done in.
In your case, you are subtracting a signed int from an unsigned int. The promotion rules say that the actual calculation is done using unsigned int
.
So your calculation is 10 - 16
in unsigned int arithmetic. Unsigned arithmetic is modulo arithmetic, meaning that it wraps around. So, assuming your typical 32-bit int, the result of this calculation is 2^32 - 6.
This is the same for both lines. Note that the subtraction is completely independent from the assignment; the type on the left side has absolutely no influence on how the calculation happens. It is a common beginner mistake to think that the type on the left side somehow influences the calculation; but float f = 5 / 6
is zero, because the division still uses integer arithmetic.
The difference, then, is what happens during the assignment. The result of the subtraction is implicitly converted to float
in one case, and int
in the other.
The conversion to float tries to find the closest value to the actual one that the type can represent. This will be some very large value; not quite the one the original subtraction yielded though.
The conversion to int says that if the value fits into the range of int, the value will be unchanged. But 2^32 - 6 is far larger than the 2^32 - 1 that a 32-bit int can hold, so you get the other part of the conversion rule, which says that the resulting value is implementation-defined. This is a term in the standard that means "different compilers can do different things, but they have to document what they do".
For all practical purposes, all compilers that you'll likely encounter say that the bit pattern stays the same and is just interpreted as signed. Because of the way 2's complement arithmetic works (the way that almost all computers represent negative numbers), the result is the -6 you would expect from the calculation.
But all this is a very long way of repeating the first point, which is "don't do mixed type arithmetic". Cast the types first, explicitly, to types that you know will do the right thing.
add a comment |Â
up vote
5
down vote
up vote
5
down vote
First, you have to understand "usual arithmetic conversions" (that link is for C, but the rules are the same in C++). In C++, if you do arithmetic with mixed types (you should avoid that when possible, by the way), there's a set of rules that decides which type the calculation is done in.
In your case, you are subtracting a signed int from an unsigned int. The promotion rules say that the actual calculation is done using unsigned int
.
So your calculation is 10 - 16
in unsigned int arithmetic. Unsigned arithmetic is modulo arithmetic, meaning that it wraps around. So, assuming your typical 32-bit int, the result of this calculation is 2^32 - 6.
This is the same for both lines. Note that the subtraction is completely independent from the assignment; the type on the left side has absolutely no influence on how the calculation happens. It is a common beginner mistake to think that the type on the left side somehow influences the calculation; but float f = 5 / 6
is zero, because the division still uses integer arithmetic.
The difference, then, is what happens during the assignment. The result of the subtraction is implicitly converted to float
in one case, and int
in the other.
The conversion to float tries to find the closest value to the actual one that the type can represent. This will be some very large value; not quite the one the original subtraction yielded though.
The conversion to int says that if the value fits into the range of int, the value will be unchanged. But 2^32 - 6 is far larger than the 2^32 - 1 that a 32-bit int can hold, so you get the other part of the conversion rule, which says that the resulting value is implementation-defined. This is a term in the standard that means "different compilers can do different things, but they have to document what they do".
For all practical purposes, all compilers that you'll likely encounter say that the bit pattern stays the same and is just interpreted as signed. Because of the way 2's complement arithmetic works (the way that almost all computers represent negative numbers), the result is the -6 you would expect from the calculation.
But all this is a very long way of repeating the first point, which is "don't do mixed type arithmetic". Cast the types first, explicitly, to types that you know will do the right thing.
First, you have to understand "usual arithmetic conversions" (that link is for C, but the rules are the same in C++). In C++, if you do arithmetic with mixed types (you should avoid that when possible, by the way), there's a set of rules that decides which type the calculation is done in.
In your case, you are subtracting a signed int from an unsigned int. The promotion rules say that the actual calculation is done using unsigned int
.
So your calculation is 10 - 16
in unsigned int arithmetic. Unsigned arithmetic is modulo arithmetic, meaning that it wraps around. So, assuming your typical 32-bit int, the result of this calculation is 2^32 - 6.
This is the same for both lines. Note that the subtraction is completely independent from the assignment; the type on the left side has absolutely no influence on how the calculation happens. It is a common beginner mistake to think that the type on the left side somehow influences the calculation; but float f = 5 / 6
is zero, because the division still uses integer arithmetic.
The difference, then, is what happens during the assignment. The result of the subtraction is implicitly converted to float
in one case, and int
in the other.
The conversion to float tries to find the closest value to the actual one that the type can represent. This will be some very large value; not quite the one the original subtraction yielded though.
The conversion to int says that if the value fits into the range of int, the value will be unchanged. But 2^32 - 6 is far larger than the 2^32 - 1 that a 32-bit int can hold, so you get the other part of the conversion rule, which says that the resulting value is implementation-defined. This is a term in the standard that means "different compilers can do different things, but they have to document what they do".
For all practical purposes, all compilers that you'll likely encounter say that the bit pattern stays the same and is just interpreted as signed. Because of the way 2's complement arithmetic works (the way that almost all computers represent negative numbers), the result is the -6 you would expect from the calculation.
But all this is a very long way of repeating the first point, which is "don't do mixed type arithmetic". Cast the types first, explicitly, to types that you know will do the right thing.
answered 16 mins ago
Sebastian Redl
48.2k474109
48.2k474109
add a comment |Â
add a comment |Â
Eugene Kolombet is a new contributor. Be nice, and check out our Code of Conduct.
Eugene Kolombet is a new contributor. Be nice, and check out our Code of Conduct.
Eugene Kolombet is a new contributor. Be nice, and check out our Code of Conduct.
Eugene Kolombet is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52754505%2fwhy-is-the-sign-different-after-substracting-unsigned-and-signed%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password