Why is the sign different after substracting unsigned and signed?

The name of the pictureThe name of the pictureThe name of the pictureClash 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?










share|improve this question









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.























    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?










    share|improve this question









    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.





















      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?










      share|improve this question









      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++






      share|improve this question









      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.











      share|improve this question









      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.









      share|improve this question




      share|improve this question








      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.






















          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.







          share|improve this answer






















          • 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

















          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.






          share|improve this answer




















            Your Answer





            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "1"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: true,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );






            Eugene Kolombet is a new contributor. Be nice, and check out our Code of Conduct.









             

            draft saved


            draft discarded


















            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






























            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.







            share|improve this answer






















            • 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














            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.







            share|improve this answer






















            • 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












            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.







            share|improve this answer














            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.








            share|improve this answer














            share|improve this answer



            share|improve this answer








            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
















            • 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












            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.






            share|improve this answer
























              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.






              share|improve this answer






















                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.






                share|improve this answer












                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.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 16 mins ago









                Sebastian Redl

                48.2k474109




                48.2k474109




















                    Eugene Kolombet is a new contributor. Be nice, and check out our Code of Conduct.









                     

                    draft saved


                    draft discarded


















                    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.













                     


                    draft saved


                    draft discarded














                    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













































































                    Comments

                    Popular posts from this blog

                    What does second last employer means? [closed]

                    List of Gilmore Girls characters

                    Confectionery