Is a negative integer summed with a greater unsigned integer promoted to unsigned int?












13















After getting advised to read "C++ Primer 5 ed by Stanley B. Lipman" I don't understand this:



Page 66. "Expressions Involving Unsigned Types"



unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl; // prints -84
std::cout << u + i << std::endl; // if 32-bit ints, prints 4294967264


He said:




In the second expression, the int value -42 is converted to unsigned before the addition is done. Converting a negative number to unsigned behaves exactly as if we had attempted to assign that negative value to an unsigned object. The value “wraps around” as described above.




But if I do something like this:



unsigned u = 42;
int i = -10;
std::cout << u + i << std::endl; // Why the result is 32?


As you can see -10 is not converted to unsigned int. Does this mean a comparison occurs before promoting a signed integer to an unsigned integer?










share|improve this question




















  • 16





    As you can see -10 is not converted to unsigned int. It is.

    – tkausl
    16 hours ago






  • 1





    Google about binary numbers and the way they are represented, in particular signedness. Then all shall become clear.

    – DeiDei
    16 hours ago






  • 1





    What result were you expecting instead of 32?

    – Barmar
    13 hours ago











  • Possibly related: c++ safeness of code with implicit conversion between signed and unsigned

    – francesco
    2 hours ago
















13















After getting advised to read "C++ Primer 5 ed by Stanley B. Lipman" I don't understand this:



Page 66. "Expressions Involving Unsigned Types"



unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl; // prints -84
std::cout << u + i << std::endl; // if 32-bit ints, prints 4294967264


He said:




In the second expression, the int value -42 is converted to unsigned before the addition is done. Converting a negative number to unsigned behaves exactly as if we had attempted to assign that negative value to an unsigned object. The value “wraps around” as described above.




But if I do something like this:



unsigned u = 42;
int i = -10;
std::cout << u + i << std::endl; // Why the result is 32?


As you can see -10 is not converted to unsigned int. Does this mean a comparison occurs before promoting a signed integer to an unsigned integer?










share|improve this question




















  • 16





    As you can see -10 is not converted to unsigned int. It is.

    – tkausl
    16 hours ago






  • 1





    Google about binary numbers and the way they are represented, in particular signedness. Then all shall become clear.

    – DeiDei
    16 hours ago






  • 1





    What result were you expecting instead of 32?

    – Barmar
    13 hours ago











  • Possibly related: c++ safeness of code with implicit conversion between signed and unsigned

    – francesco
    2 hours ago














13












13








13


3






After getting advised to read "C++ Primer 5 ed by Stanley B. Lipman" I don't understand this:



Page 66. "Expressions Involving Unsigned Types"



unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl; // prints -84
std::cout << u + i << std::endl; // if 32-bit ints, prints 4294967264


He said:




In the second expression, the int value -42 is converted to unsigned before the addition is done. Converting a negative number to unsigned behaves exactly as if we had attempted to assign that negative value to an unsigned object. The value “wraps around” as described above.




But if I do something like this:



unsigned u = 42;
int i = -10;
std::cout << u + i << std::endl; // Why the result is 32?


As you can see -10 is not converted to unsigned int. Does this mean a comparison occurs before promoting a signed integer to an unsigned integer?










share|improve this question
















After getting advised to read "C++ Primer 5 ed by Stanley B. Lipman" I don't understand this:



Page 66. "Expressions Involving Unsigned Types"



unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl; // prints -84
std::cout << u + i << std::endl; // if 32-bit ints, prints 4294967264


He said:




In the second expression, the int value -42 is converted to unsigned before the addition is done. Converting a negative number to unsigned behaves exactly as if we had attempted to assign that negative value to an unsigned object. The value “wraps around” as described above.




But if I do something like this:



unsigned u = 42;
int i = -10;
std::cout << u + i << std::endl; // Why the result is 32?


As you can see -10 is not converted to unsigned int. Does this mean a comparison occurs before promoting a signed integer to an unsigned integer?







c++ unsigned-integer






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 4 hours ago









Lightness Races in Orbit

286k51464788




286k51464788










asked 16 hours ago









Alex24Alex24

1438




1438








  • 16





    As you can see -10 is not converted to unsigned int. It is.

    – tkausl
    16 hours ago






  • 1





    Google about binary numbers and the way they are represented, in particular signedness. Then all shall become clear.

    – DeiDei
    16 hours ago






  • 1





    What result were you expecting instead of 32?

    – Barmar
    13 hours ago











  • Possibly related: c++ safeness of code with implicit conversion between signed and unsigned

    – francesco
    2 hours ago














  • 16





    As you can see -10 is not converted to unsigned int. It is.

    – tkausl
    16 hours ago






  • 1





    Google about binary numbers and the way they are represented, in particular signedness. Then all shall become clear.

    – DeiDei
    16 hours ago






  • 1





    What result were you expecting instead of 32?

    – Barmar
    13 hours ago











  • Possibly related: c++ safeness of code with implicit conversion between signed and unsigned

    – francesco
    2 hours ago








16




16





As you can see -10 is not converted to unsigned int. It is.

– tkausl
16 hours ago





As you can see -10 is not converted to unsigned int. It is.

– tkausl
16 hours ago




1




1





Google about binary numbers and the way they are represented, in particular signedness. Then all shall become clear.

– DeiDei
16 hours ago





Google about binary numbers and the way they are represented, in particular signedness. Then all shall become clear.

– DeiDei
16 hours ago




1




1





What result were you expecting instead of 32?

– Barmar
13 hours ago





What result were you expecting instead of 32?

– Barmar
13 hours ago













Possibly related: c++ safeness of code with implicit conversion between signed and unsigned

– francesco
2 hours ago





Possibly related: c++ safeness of code with implicit conversion between signed and unsigned

– francesco
2 hours ago












5 Answers
5






active

oldest

votes


















23














-10 is being converted to a unsigned integer with a very large value, the reason you get a small number is that the addition wraps you back around. With 32 bit unsigned integers -10 is the same as 4294967286. When you add 42 to that you get 4294967328, but the max value is 4294967296, so we have to take 4294967328 modulo 4294967296 and we get 32.






share|improve this answer


























  • What I find interesting with modulo arithmetic is that by "convoluted" means, you get the same answer as regular arithmetic. This is one reason to justify wrapping behavior for overflow handling.

    – Matthieu M.
    5 hours ago











  • @MatthieuM. The "convolution" only happens when you think of unsigned integers as numbers. The fact that adding a number from the equivalence class of 42 to a number from the equivalence class of -10 yields a value from the equivalence class of 32 is no less intuitive than the signed result IMO.

    – Baum mit Augen
    2 hours ago





















15














Well, I guess this is an exception to "two wrongs don't make a right" :)



What is happening is that there are actually two wrap arounds (unsigned overflows) under the hood and the final result is ends up being mathematically correct.




  • First, i is converted to unsigned and as per wrap around behavior the value is std::numeric_limits<unsigned>::max() - 9.


  • When this value is summed with u the mathematical result would be std::numeric_limits<unsigned>::max() - 9 + 42 == std::numeric_limits<unsigned>::max() + 33 which is an overflow and we get another wrap around. So the final result is 32.





As a general rule in an arithmetic expression if you only have unsigned overflows (no matter how many) and if the final mathematical result is representable in the expression data type, then the value of the expression will be the mathematically correct one. This is a consequence of the fact that unsigned integers in C++ obey the laws of arithmetic modulo 2n (see bellow).





Important notice. According to C++ unsigned arithmetic does not overflow:




§6.9.1 Fundamental types [basic.fundamental]




  1. Unsigned integers shall obey the laws of arithmetic modulo 2n where n
    is the number of bits in the value representation of that particular
    size of integer 49


49) This implies that unsigned arithmetic does not overflow because a
result that cannot be represented by the resulting unsigned integer
type is reduced modulo the number that is one greater than the largest
value that can be represented by the resulting unsigned integer type.




I will however leave "overflow" in my answer to signify values that cannot be represented in regular arithmetic.



Also what we colloquially call "wrap around" is in fact just the arithmetic modulo nature of the unsigned integers. I will however use "wrap around" also because it is easier to understand.






share|improve this answer





















  • 3





    @BaummitAugen what? of course it overflows. Add two large unsigned ints, you can't represent a number that doesn't fit in the number format

    – Garr Godfrey
    16 hours ago






  • 1





    it's a bit ridiculous to argue losing the high order bits doesn't count as overflow just because the low order bits are still accurate.

    – Garr Godfrey
    16 hours ago






  • 2





    @GarrGodfrey I searched the standard and he is indeed correct. There is no overflow. And this is because unsigned integers do not represent natural numbers, but, as BaummitAugen said, they represent equivalence classes.

    – bolov
    15 hours ago








  • 2





    @curiousguy I've heard some ppl (experts) saying that having wraparound behavior for unsigned was a bad decision in hindsight. They main reason however was the optimization it inhibits. They argued for regular unsigned to have undefined behavior on overflow and to exist some other data type that had wraparound behavior. But it is what it is.

    – bolov
    13 hours ago








  • 2





    @curiousguy "then why would anyone use them to represent a number of bytes, a number of objects in a container, etc.?" Several prominent members of a committee consider exactly that a historical mistake. channel9.msdn.com/Events/GoingNative/2013/… 9:50, 42:40, 1:02:50

    – Baum mit Augen
    5 hours ago



















4














i is in fact promoted to unsigned int.



Unsigned integers in C and C++ implement arithmetic in ℤ / 2nℤ, where n is the number of bits in the unsigned integer type. Thus we get



[42] + [-10] ≡ [42] + [2n - 10] ≡ [2n + 32] ≡ [32],



with [x] denoting the equivalence class of x in ℤ / 2nℤ.



Of course, the intermediate step of picking only non-negative representatives of each equivalence class, while it formally occurs, is not necessary to explain the result; the immediate



[42] + [-10] ≡ [32]



would also be correct.






share|improve this answer


























  • Shouldn't that be ℤ mod 2n?

    – JAD
    7 hours ago











  • @JAD No. It is correct as it stands.

    – Baum mit Augen
    5 hours ago






  • 1





    @JAD Quotient ring.

    – user202729
    5 hours ago






  • 1





    @JAD That should be ℤ / 2ⁿ ℤ

    – Eric Duminil
    44 mins ago











  • @EricDuminil True, thanks.

    – Baum mit Augen
    42 mins ago



















3















"In the second expression, the int value -42 is converted to unsigned before the addition is done"




yes this is true




unsigned u = 42;
int i = -10;
std::cout << u + i << std::endl; // Why the result is 32?



Supposing we are in 32 bits (that change nothing in 64b, this is just to explain) this is computed as 42u + ((unsigned) -10) so 42u + 4294967286u and the result is 4294967328u truncated in 32 bits so 32. All was done in unsigned






share|improve this answer































    2














    This is part of what is wonderful about 2's complement representation. The processor doesn't know or care if a number is signed or unsigned, the operations are the same. In both cases, the calculation is correct. It's only how the binary number is interpreted after the fact, when printing, that is actually matters (there may be other cases, as with comparison operators)



    -10 in 32BIT binary is FFFFFFF6
    42 IN 32bit BINARY is 0000002A


    Adding them together, it doesn't matter to the processor if they are signed or unsigned, the result is: 100000020. In 32bit, the 1 at the start will be placed in the overflow register, and in c++ is just disappears. You get 0x20 as the result, which is 32.



    In the first case, it is basically the same:



    -42 in 32BIT binary is FFFFFFD6
    10 IN 32bit binary is 0000000A


    Add those together and get FFFFFFE0



    FFFFFFE0 as a signed int is -32 (decimal). The calculation is correct! But, because it is being PRINTED as an unsigned, it shows up as 4294967264. It's about interpreting the result.






    share|improve this answer
























    • As a side note, on x86, most arithmetic operations set various bits in the flags register, which can then be used to e.g. perform branching. For example, addition would set CF (carry flag) to signify that unsigned "wrapping" has occurred, and/or OF (overflow flag) to signify that signed overflow has occurred, if you were to consider the operands signed. There was even a 1-byte INTO instruction which generated a software interrupt if OF was set, which could help with debugging, but unfortunately was not supported from higher level languages, and is no longer available in 64 bit mode.

      – Arne Vogel
      4 hours ago






    • 1





      Technically speaking, nothing mandates 2's-complement representation. It's just that the modular-arithmetic conversion specified by the standard is exactly equivalent to using 2's-complement (as you'll see if you perform the conversion on a sign-magnitude or 1's-complement system, if it's correct).

      – Toby Speight
      3 hours ago











    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',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54190113%2fis-a-negative-integer-summed-with-a-greater-unsigned-integer-promoted-to-unsigne%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    5 Answers
    5






    active

    oldest

    votes








    5 Answers
    5






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    23














    -10 is being converted to a unsigned integer with a very large value, the reason you get a small number is that the addition wraps you back around. With 32 bit unsigned integers -10 is the same as 4294967286. When you add 42 to that you get 4294967328, but the max value is 4294967296, so we have to take 4294967328 modulo 4294967296 and we get 32.






    share|improve this answer


























    • What I find interesting with modulo arithmetic is that by "convoluted" means, you get the same answer as regular arithmetic. This is one reason to justify wrapping behavior for overflow handling.

      – Matthieu M.
      5 hours ago











    • @MatthieuM. The "convolution" only happens when you think of unsigned integers as numbers. The fact that adding a number from the equivalence class of 42 to a number from the equivalence class of -10 yields a value from the equivalence class of 32 is no less intuitive than the signed result IMO.

      – Baum mit Augen
      2 hours ago


















    23














    -10 is being converted to a unsigned integer with a very large value, the reason you get a small number is that the addition wraps you back around. With 32 bit unsigned integers -10 is the same as 4294967286. When you add 42 to that you get 4294967328, but the max value is 4294967296, so we have to take 4294967328 modulo 4294967296 and we get 32.






    share|improve this answer


























    • What I find interesting with modulo arithmetic is that by "convoluted" means, you get the same answer as regular arithmetic. This is one reason to justify wrapping behavior for overflow handling.

      – Matthieu M.
      5 hours ago











    • @MatthieuM. The "convolution" only happens when you think of unsigned integers as numbers. The fact that adding a number from the equivalence class of 42 to a number from the equivalence class of -10 yields a value from the equivalence class of 32 is no less intuitive than the signed result IMO.

      – Baum mit Augen
      2 hours ago
















    23












    23








    23







    -10 is being converted to a unsigned integer with a very large value, the reason you get a small number is that the addition wraps you back around. With 32 bit unsigned integers -10 is the same as 4294967286. When you add 42 to that you get 4294967328, but the max value is 4294967296, so we have to take 4294967328 modulo 4294967296 and we get 32.






    share|improve this answer















    -10 is being converted to a unsigned integer with a very large value, the reason you get a small number is that the addition wraps you back around. With 32 bit unsigned integers -10 is the same as 4294967286. When you add 42 to that you get 4294967328, but the max value is 4294967296, so we have to take 4294967328 modulo 4294967296 and we get 32.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited 16 hours ago

























    answered 16 hours ago









    NathanOliverNathanOliver

    88.9k15120186




    88.9k15120186













    • What I find interesting with modulo arithmetic is that by "convoluted" means, you get the same answer as regular arithmetic. This is one reason to justify wrapping behavior for overflow handling.

      – Matthieu M.
      5 hours ago











    • @MatthieuM. The "convolution" only happens when you think of unsigned integers as numbers. The fact that adding a number from the equivalence class of 42 to a number from the equivalence class of -10 yields a value from the equivalence class of 32 is no less intuitive than the signed result IMO.

      – Baum mit Augen
      2 hours ago





















    • What I find interesting with modulo arithmetic is that by "convoluted" means, you get the same answer as regular arithmetic. This is one reason to justify wrapping behavior for overflow handling.

      – Matthieu M.
      5 hours ago











    • @MatthieuM. The "convolution" only happens when you think of unsigned integers as numbers. The fact that adding a number from the equivalence class of 42 to a number from the equivalence class of -10 yields a value from the equivalence class of 32 is no less intuitive than the signed result IMO.

      – Baum mit Augen
      2 hours ago



















    What I find interesting with modulo arithmetic is that by "convoluted" means, you get the same answer as regular arithmetic. This is one reason to justify wrapping behavior for overflow handling.

    – Matthieu M.
    5 hours ago





    What I find interesting with modulo arithmetic is that by "convoluted" means, you get the same answer as regular arithmetic. This is one reason to justify wrapping behavior for overflow handling.

    – Matthieu M.
    5 hours ago













    @MatthieuM. The "convolution" only happens when you think of unsigned integers as numbers. The fact that adding a number from the equivalence class of 42 to a number from the equivalence class of -10 yields a value from the equivalence class of 32 is no less intuitive than the signed result IMO.

    – Baum mit Augen
    2 hours ago







    @MatthieuM. The "convolution" only happens when you think of unsigned integers as numbers. The fact that adding a number from the equivalence class of 42 to a number from the equivalence class of -10 yields a value from the equivalence class of 32 is no less intuitive than the signed result IMO.

    – Baum mit Augen
    2 hours ago















    15














    Well, I guess this is an exception to "two wrongs don't make a right" :)



    What is happening is that there are actually two wrap arounds (unsigned overflows) under the hood and the final result is ends up being mathematically correct.




    • First, i is converted to unsigned and as per wrap around behavior the value is std::numeric_limits<unsigned>::max() - 9.


    • When this value is summed with u the mathematical result would be std::numeric_limits<unsigned>::max() - 9 + 42 == std::numeric_limits<unsigned>::max() + 33 which is an overflow and we get another wrap around. So the final result is 32.





    As a general rule in an arithmetic expression if you only have unsigned overflows (no matter how many) and if the final mathematical result is representable in the expression data type, then the value of the expression will be the mathematically correct one. This is a consequence of the fact that unsigned integers in C++ obey the laws of arithmetic modulo 2n (see bellow).





    Important notice. According to C++ unsigned arithmetic does not overflow:




    §6.9.1 Fundamental types [basic.fundamental]




    1. Unsigned integers shall obey the laws of arithmetic modulo 2n where n
      is the number of bits in the value representation of that particular
      size of integer 49


    49) This implies that unsigned arithmetic does not overflow because a
    result that cannot be represented by the resulting unsigned integer
    type is reduced modulo the number that is one greater than the largest
    value that can be represented by the resulting unsigned integer type.




    I will however leave "overflow" in my answer to signify values that cannot be represented in regular arithmetic.



    Also what we colloquially call "wrap around" is in fact just the arithmetic modulo nature of the unsigned integers. I will however use "wrap around" also because it is easier to understand.






    share|improve this answer





















    • 3





      @BaummitAugen what? of course it overflows. Add two large unsigned ints, you can't represent a number that doesn't fit in the number format

      – Garr Godfrey
      16 hours ago






    • 1





      it's a bit ridiculous to argue losing the high order bits doesn't count as overflow just because the low order bits are still accurate.

      – Garr Godfrey
      16 hours ago






    • 2





      @GarrGodfrey I searched the standard and he is indeed correct. There is no overflow. And this is because unsigned integers do not represent natural numbers, but, as BaummitAugen said, they represent equivalence classes.

      – bolov
      15 hours ago








    • 2





      @curiousguy I've heard some ppl (experts) saying that having wraparound behavior for unsigned was a bad decision in hindsight. They main reason however was the optimization it inhibits. They argued for regular unsigned to have undefined behavior on overflow and to exist some other data type that had wraparound behavior. But it is what it is.

      – bolov
      13 hours ago








    • 2





      @curiousguy "then why would anyone use them to represent a number of bytes, a number of objects in a container, etc.?" Several prominent members of a committee consider exactly that a historical mistake. channel9.msdn.com/Events/GoingNative/2013/… 9:50, 42:40, 1:02:50

      – Baum mit Augen
      5 hours ago
















    15














    Well, I guess this is an exception to "two wrongs don't make a right" :)



    What is happening is that there are actually two wrap arounds (unsigned overflows) under the hood and the final result is ends up being mathematically correct.




    • First, i is converted to unsigned and as per wrap around behavior the value is std::numeric_limits<unsigned>::max() - 9.


    • When this value is summed with u the mathematical result would be std::numeric_limits<unsigned>::max() - 9 + 42 == std::numeric_limits<unsigned>::max() + 33 which is an overflow and we get another wrap around. So the final result is 32.





    As a general rule in an arithmetic expression if you only have unsigned overflows (no matter how many) and if the final mathematical result is representable in the expression data type, then the value of the expression will be the mathematically correct one. This is a consequence of the fact that unsigned integers in C++ obey the laws of arithmetic modulo 2n (see bellow).





    Important notice. According to C++ unsigned arithmetic does not overflow:




    §6.9.1 Fundamental types [basic.fundamental]




    1. Unsigned integers shall obey the laws of arithmetic modulo 2n where n
      is the number of bits in the value representation of that particular
      size of integer 49


    49) This implies that unsigned arithmetic does not overflow because a
    result that cannot be represented by the resulting unsigned integer
    type is reduced modulo the number that is one greater than the largest
    value that can be represented by the resulting unsigned integer type.




    I will however leave "overflow" in my answer to signify values that cannot be represented in regular arithmetic.



    Also what we colloquially call "wrap around" is in fact just the arithmetic modulo nature of the unsigned integers. I will however use "wrap around" also because it is easier to understand.






    share|improve this answer





















    • 3





      @BaummitAugen what? of course it overflows. Add two large unsigned ints, you can't represent a number that doesn't fit in the number format

      – Garr Godfrey
      16 hours ago






    • 1





      it's a bit ridiculous to argue losing the high order bits doesn't count as overflow just because the low order bits are still accurate.

      – Garr Godfrey
      16 hours ago






    • 2





      @GarrGodfrey I searched the standard and he is indeed correct. There is no overflow. And this is because unsigned integers do not represent natural numbers, but, as BaummitAugen said, they represent equivalence classes.

      – bolov
      15 hours ago








    • 2





      @curiousguy I've heard some ppl (experts) saying that having wraparound behavior for unsigned was a bad decision in hindsight. They main reason however was the optimization it inhibits. They argued for regular unsigned to have undefined behavior on overflow and to exist some other data type that had wraparound behavior. But it is what it is.

      – bolov
      13 hours ago








    • 2





      @curiousguy "then why would anyone use them to represent a number of bytes, a number of objects in a container, etc.?" Several prominent members of a committee consider exactly that a historical mistake. channel9.msdn.com/Events/GoingNative/2013/… 9:50, 42:40, 1:02:50

      – Baum mit Augen
      5 hours ago














    15












    15








    15







    Well, I guess this is an exception to "two wrongs don't make a right" :)



    What is happening is that there are actually two wrap arounds (unsigned overflows) under the hood and the final result is ends up being mathematically correct.




    • First, i is converted to unsigned and as per wrap around behavior the value is std::numeric_limits<unsigned>::max() - 9.


    • When this value is summed with u the mathematical result would be std::numeric_limits<unsigned>::max() - 9 + 42 == std::numeric_limits<unsigned>::max() + 33 which is an overflow and we get another wrap around. So the final result is 32.





    As a general rule in an arithmetic expression if you only have unsigned overflows (no matter how many) and if the final mathematical result is representable in the expression data type, then the value of the expression will be the mathematically correct one. This is a consequence of the fact that unsigned integers in C++ obey the laws of arithmetic modulo 2n (see bellow).





    Important notice. According to C++ unsigned arithmetic does not overflow:




    §6.9.1 Fundamental types [basic.fundamental]




    1. Unsigned integers shall obey the laws of arithmetic modulo 2n where n
      is the number of bits in the value representation of that particular
      size of integer 49


    49) This implies that unsigned arithmetic does not overflow because a
    result that cannot be represented by the resulting unsigned integer
    type is reduced modulo the number that is one greater than the largest
    value that can be represented by the resulting unsigned integer type.




    I will however leave "overflow" in my answer to signify values that cannot be represented in regular arithmetic.



    Also what we colloquially call "wrap around" is in fact just the arithmetic modulo nature of the unsigned integers. I will however use "wrap around" also because it is easier to understand.






    share|improve this answer















    Well, I guess this is an exception to "two wrongs don't make a right" :)



    What is happening is that there are actually two wrap arounds (unsigned overflows) under the hood and the final result is ends up being mathematically correct.




    • First, i is converted to unsigned and as per wrap around behavior the value is std::numeric_limits<unsigned>::max() - 9.


    • When this value is summed with u the mathematical result would be std::numeric_limits<unsigned>::max() - 9 + 42 == std::numeric_limits<unsigned>::max() + 33 which is an overflow and we get another wrap around. So the final result is 32.





    As a general rule in an arithmetic expression if you only have unsigned overflows (no matter how many) and if the final mathematical result is representable in the expression data type, then the value of the expression will be the mathematically correct one. This is a consequence of the fact that unsigned integers in C++ obey the laws of arithmetic modulo 2n (see bellow).





    Important notice. According to C++ unsigned arithmetic does not overflow:




    §6.9.1 Fundamental types [basic.fundamental]




    1. Unsigned integers shall obey the laws of arithmetic modulo 2n where n
      is the number of bits in the value representation of that particular
      size of integer 49


    49) This implies that unsigned arithmetic does not overflow because a
    result that cannot be represented by the resulting unsigned integer
    type is reduced modulo the number that is one greater than the largest
    value that can be represented by the resulting unsigned integer type.




    I will however leave "overflow" in my answer to signify values that cannot be represented in regular arithmetic.



    Also what we colloquially call "wrap around" is in fact just the arithmetic modulo nature of the unsigned integers. I will however use "wrap around" also because it is easier to understand.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited 2 hours ago

























    answered 16 hours ago









    bolovbolov

    31k669129




    31k669129








    • 3





      @BaummitAugen what? of course it overflows. Add two large unsigned ints, you can't represent a number that doesn't fit in the number format

      – Garr Godfrey
      16 hours ago






    • 1





      it's a bit ridiculous to argue losing the high order bits doesn't count as overflow just because the low order bits are still accurate.

      – Garr Godfrey
      16 hours ago






    • 2





      @GarrGodfrey I searched the standard and he is indeed correct. There is no overflow. And this is because unsigned integers do not represent natural numbers, but, as BaummitAugen said, they represent equivalence classes.

      – bolov
      15 hours ago








    • 2





      @curiousguy I've heard some ppl (experts) saying that having wraparound behavior for unsigned was a bad decision in hindsight. They main reason however was the optimization it inhibits. They argued for regular unsigned to have undefined behavior on overflow and to exist some other data type that had wraparound behavior. But it is what it is.

      – bolov
      13 hours ago








    • 2





      @curiousguy "then why would anyone use them to represent a number of bytes, a number of objects in a container, etc.?" Several prominent members of a committee consider exactly that a historical mistake. channel9.msdn.com/Events/GoingNative/2013/… 9:50, 42:40, 1:02:50

      – Baum mit Augen
      5 hours ago














    • 3





      @BaummitAugen what? of course it overflows. Add two large unsigned ints, you can't represent a number that doesn't fit in the number format

      – Garr Godfrey
      16 hours ago






    • 1





      it's a bit ridiculous to argue losing the high order bits doesn't count as overflow just because the low order bits are still accurate.

      – Garr Godfrey
      16 hours ago






    • 2





      @GarrGodfrey I searched the standard and he is indeed correct. There is no overflow. And this is because unsigned integers do not represent natural numbers, but, as BaummitAugen said, they represent equivalence classes.

      – bolov
      15 hours ago








    • 2





      @curiousguy I've heard some ppl (experts) saying that having wraparound behavior for unsigned was a bad decision in hindsight. They main reason however was the optimization it inhibits. They argued for regular unsigned to have undefined behavior on overflow and to exist some other data type that had wraparound behavior. But it is what it is.

      – bolov
      13 hours ago








    • 2





      @curiousguy "then why would anyone use them to represent a number of bytes, a number of objects in a container, etc.?" Several prominent members of a committee consider exactly that a historical mistake. channel9.msdn.com/Events/GoingNative/2013/… 9:50, 42:40, 1:02:50

      – Baum mit Augen
      5 hours ago








    3




    3





    @BaummitAugen what? of course it overflows. Add two large unsigned ints, you can't represent a number that doesn't fit in the number format

    – Garr Godfrey
    16 hours ago





    @BaummitAugen what? of course it overflows. Add two large unsigned ints, you can't represent a number that doesn't fit in the number format

    – Garr Godfrey
    16 hours ago




    1




    1





    it's a bit ridiculous to argue losing the high order bits doesn't count as overflow just because the low order bits are still accurate.

    – Garr Godfrey
    16 hours ago





    it's a bit ridiculous to argue losing the high order bits doesn't count as overflow just because the low order bits are still accurate.

    – Garr Godfrey
    16 hours ago




    2




    2





    @GarrGodfrey I searched the standard and he is indeed correct. There is no overflow. And this is because unsigned integers do not represent natural numbers, but, as BaummitAugen said, they represent equivalence classes.

    – bolov
    15 hours ago







    @GarrGodfrey I searched the standard and he is indeed correct. There is no overflow. And this is because unsigned integers do not represent natural numbers, but, as BaummitAugen said, they represent equivalence classes.

    – bolov
    15 hours ago






    2




    2





    @curiousguy I've heard some ppl (experts) saying that having wraparound behavior for unsigned was a bad decision in hindsight. They main reason however was the optimization it inhibits. They argued for regular unsigned to have undefined behavior on overflow and to exist some other data type that had wraparound behavior. But it is what it is.

    – bolov
    13 hours ago







    @curiousguy I've heard some ppl (experts) saying that having wraparound behavior for unsigned was a bad decision in hindsight. They main reason however was the optimization it inhibits. They argued for regular unsigned to have undefined behavior on overflow and to exist some other data type that had wraparound behavior. But it is what it is.

    – bolov
    13 hours ago






    2




    2





    @curiousguy "then why would anyone use them to represent a number of bytes, a number of objects in a container, etc.?" Several prominent members of a committee consider exactly that a historical mistake. channel9.msdn.com/Events/GoingNative/2013/… 9:50, 42:40, 1:02:50

    – Baum mit Augen
    5 hours ago





    @curiousguy "then why would anyone use them to represent a number of bytes, a number of objects in a container, etc.?" Several prominent members of a committee consider exactly that a historical mistake. channel9.msdn.com/Events/GoingNative/2013/… 9:50, 42:40, 1:02:50

    – Baum mit Augen
    5 hours ago











    4














    i is in fact promoted to unsigned int.



    Unsigned integers in C and C++ implement arithmetic in ℤ / 2nℤ, where n is the number of bits in the unsigned integer type. Thus we get



    [42] + [-10] ≡ [42] + [2n - 10] ≡ [2n + 32] ≡ [32],



    with [x] denoting the equivalence class of x in ℤ / 2nℤ.



    Of course, the intermediate step of picking only non-negative representatives of each equivalence class, while it formally occurs, is not necessary to explain the result; the immediate



    [42] + [-10] ≡ [32]



    would also be correct.






    share|improve this answer


























    • Shouldn't that be ℤ mod 2n?

      – JAD
      7 hours ago











    • @JAD No. It is correct as it stands.

      – Baum mit Augen
      5 hours ago






    • 1





      @JAD Quotient ring.

      – user202729
      5 hours ago






    • 1





      @JAD That should be ℤ / 2ⁿ ℤ

      – Eric Duminil
      44 mins ago











    • @EricDuminil True, thanks.

      – Baum mit Augen
      42 mins ago
















    4














    i is in fact promoted to unsigned int.



    Unsigned integers in C and C++ implement arithmetic in ℤ / 2nℤ, where n is the number of bits in the unsigned integer type. Thus we get



    [42] + [-10] ≡ [42] + [2n - 10] ≡ [2n + 32] ≡ [32],



    with [x] denoting the equivalence class of x in ℤ / 2nℤ.



    Of course, the intermediate step of picking only non-negative representatives of each equivalence class, while it formally occurs, is not necessary to explain the result; the immediate



    [42] + [-10] ≡ [32]



    would also be correct.






    share|improve this answer


























    • Shouldn't that be ℤ mod 2n?

      – JAD
      7 hours ago











    • @JAD No. It is correct as it stands.

      – Baum mit Augen
      5 hours ago






    • 1





      @JAD Quotient ring.

      – user202729
      5 hours ago






    • 1





      @JAD That should be ℤ / 2ⁿ ℤ

      – Eric Duminil
      44 mins ago











    • @EricDuminil True, thanks.

      – Baum mit Augen
      42 mins ago














    4












    4








    4







    i is in fact promoted to unsigned int.



    Unsigned integers in C and C++ implement arithmetic in ℤ / 2nℤ, where n is the number of bits in the unsigned integer type. Thus we get



    [42] + [-10] ≡ [42] + [2n - 10] ≡ [2n + 32] ≡ [32],



    with [x] denoting the equivalence class of x in ℤ / 2nℤ.



    Of course, the intermediate step of picking only non-negative representatives of each equivalence class, while it formally occurs, is not necessary to explain the result; the immediate



    [42] + [-10] ≡ [32]



    would also be correct.






    share|improve this answer















    i is in fact promoted to unsigned int.



    Unsigned integers in C and C++ implement arithmetic in ℤ / 2nℤ, where n is the number of bits in the unsigned integer type. Thus we get



    [42] + [-10] ≡ [42] + [2n - 10] ≡ [2n + 32] ≡ [32],



    with [x] denoting the equivalence class of x in ℤ / 2nℤ.



    Of course, the intermediate step of picking only non-negative representatives of each equivalence class, while it formally occurs, is not necessary to explain the result; the immediate



    [42] + [-10] ≡ [32]



    would also be correct.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited 42 mins ago

























    answered 16 hours ago









    Baum mit AugenBaum mit Augen

    40.4k12115147




    40.4k12115147













    • Shouldn't that be ℤ mod 2n?

      – JAD
      7 hours ago











    • @JAD No. It is correct as it stands.

      – Baum mit Augen
      5 hours ago






    • 1





      @JAD Quotient ring.

      – user202729
      5 hours ago






    • 1





      @JAD That should be ℤ / 2ⁿ ℤ

      – Eric Duminil
      44 mins ago











    • @EricDuminil True, thanks.

      – Baum mit Augen
      42 mins ago



















    • Shouldn't that be ℤ mod 2n?

      – JAD
      7 hours ago











    • @JAD No. It is correct as it stands.

      – Baum mit Augen
      5 hours ago






    • 1





      @JAD Quotient ring.

      – user202729
      5 hours ago






    • 1





      @JAD That should be ℤ / 2ⁿ ℤ

      – Eric Duminil
      44 mins ago











    • @EricDuminil True, thanks.

      – Baum mit Augen
      42 mins ago

















    Shouldn't that be ℤ mod 2n?

    – JAD
    7 hours ago





    Shouldn't that be ℤ mod 2n?

    – JAD
    7 hours ago













    @JAD No. It is correct as it stands.

    – Baum mit Augen
    5 hours ago





    @JAD No. It is correct as it stands.

    – Baum mit Augen
    5 hours ago




    1




    1





    @JAD Quotient ring.

    – user202729
    5 hours ago





    @JAD Quotient ring.

    – user202729
    5 hours ago




    1




    1





    @JAD That should be ℤ / 2ⁿ ℤ

    – Eric Duminil
    44 mins ago





    @JAD That should be ℤ / 2ⁿ ℤ

    – Eric Duminil
    44 mins ago













    @EricDuminil True, thanks.

    – Baum mit Augen
    42 mins ago





    @EricDuminil True, thanks.

    – Baum mit Augen
    42 mins ago











    3















    "In the second expression, the int value -42 is converted to unsigned before the addition is done"




    yes this is true




    unsigned u = 42;
    int i = -10;
    std::cout << u + i << std::endl; // Why the result is 32?



    Supposing we are in 32 bits (that change nothing in 64b, this is just to explain) this is computed as 42u + ((unsigned) -10) so 42u + 4294967286u and the result is 4294967328u truncated in 32 bits so 32. All was done in unsigned






    share|improve this answer




























      3















      "In the second expression, the int value -42 is converted to unsigned before the addition is done"




      yes this is true




      unsigned u = 42;
      int i = -10;
      std::cout << u + i << std::endl; // Why the result is 32?



      Supposing we are in 32 bits (that change nothing in 64b, this is just to explain) this is computed as 42u + ((unsigned) -10) so 42u + 4294967286u and the result is 4294967328u truncated in 32 bits so 32. All was done in unsigned






      share|improve this answer


























        3












        3








        3








        "In the second expression, the int value -42 is converted to unsigned before the addition is done"




        yes this is true




        unsigned u = 42;
        int i = -10;
        std::cout << u + i << std::endl; // Why the result is 32?



        Supposing we are in 32 bits (that change nothing in 64b, this is just to explain) this is computed as 42u + ((unsigned) -10) so 42u + 4294967286u and the result is 4294967328u truncated in 32 bits so 32. All was done in unsigned






        share|improve this answer














        "In the second expression, the int value -42 is converted to unsigned before the addition is done"




        yes this is true




        unsigned u = 42;
        int i = -10;
        std::cout << u + i << std::endl; // Why the result is 32?



        Supposing we are in 32 bits (that change nothing in 64b, this is just to explain) this is computed as 42u + ((unsigned) -10) so 42u + 4294967286u and the result is 4294967328u truncated in 32 bits so 32. All was done in unsigned







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered 16 hours ago









        brunobruno

        3,3741817




        3,3741817























            2














            This is part of what is wonderful about 2's complement representation. The processor doesn't know or care if a number is signed or unsigned, the operations are the same. In both cases, the calculation is correct. It's only how the binary number is interpreted after the fact, when printing, that is actually matters (there may be other cases, as with comparison operators)



            -10 in 32BIT binary is FFFFFFF6
            42 IN 32bit BINARY is 0000002A


            Adding them together, it doesn't matter to the processor if they are signed or unsigned, the result is: 100000020. In 32bit, the 1 at the start will be placed in the overflow register, and in c++ is just disappears. You get 0x20 as the result, which is 32.



            In the first case, it is basically the same:



            -42 in 32BIT binary is FFFFFFD6
            10 IN 32bit binary is 0000000A


            Add those together and get FFFFFFE0



            FFFFFFE0 as a signed int is -32 (decimal). The calculation is correct! But, because it is being PRINTED as an unsigned, it shows up as 4294967264. It's about interpreting the result.






            share|improve this answer
























            • As a side note, on x86, most arithmetic operations set various bits in the flags register, which can then be used to e.g. perform branching. For example, addition would set CF (carry flag) to signify that unsigned "wrapping" has occurred, and/or OF (overflow flag) to signify that signed overflow has occurred, if you were to consider the operands signed. There was even a 1-byte INTO instruction which generated a software interrupt if OF was set, which could help with debugging, but unfortunately was not supported from higher level languages, and is no longer available in 64 bit mode.

              – Arne Vogel
              4 hours ago






            • 1





              Technically speaking, nothing mandates 2's-complement representation. It's just that the modular-arithmetic conversion specified by the standard is exactly equivalent to using 2's-complement (as you'll see if you perform the conversion on a sign-magnitude or 1's-complement system, if it's correct).

              – Toby Speight
              3 hours ago
















            2














            This is part of what is wonderful about 2's complement representation. The processor doesn't know or care if a number is signed or unsigned, the operations are the same. In both cases, the calculation is correct. It's only how the binary number is interpreted after the fact, when printing, that is actually matters (there may be other cases, as with comparison operators)



            -10 in 32BIT binary is FFFFFFF6
            42 IN 32bit BINARY is 0000002A


            Adding them together, it doesn't matter to the processor if they are signed or unsigned, the result is: 100000020. In 32bit, the 1 at the start will be placed in the overflow register, and in c++ is just disappears. You get 0x20 as the result, which is 32.



            In the first case, it is basically the same:



            -42 in 32BIT binary is FFFFFFD6
            10 IN 32bit binary is 0000000A


            Add those together and get FFFFFFE0



            FFFFFFE0 as a signed int is -32 (decimal). The calculation is correct! But, because it is being PRINTED as an unsigned, it shows up as 4294967264. It's about interpreting the result.






            share|improve this answer
























            • As a side note, on x86, most arithmetic operations set various bits in the flags register, which can then be used to e.g. perform branching. For example, addition would set CF (carry flag) to signify that unsigned "wrapping" has occurred, and/or OF (overflow flag) to signify that signed overflow has occurred, if you were to consider the operands signed. There was even a 1-byte INTO instruction which generated a software interrupt if OF was set, which could help with debugging, but unfortunately was not supported from higher level languages, and is no longer available in 64 bit mode.

              – Arne Vogel
              4 hours ago






            • 1





              Technically speaking, nothing mandates 2's-complement representation. It's just that the modular-arithmetic conversion specified by the standard is exactly equivalent to using 2's-complement (as you'll see if you perform the conversion on a sign-magnitude or 1's-complement system, if it's correct).

              – Toby Speight
              3 hours ago














            2












            2








            2







            This is part of what is wonderful about 2's complement representation. The processor doesn't know or care if a number is signed or unsigned, the operations are the same. In both cases, the calculation is correct. It's only how the binary number is interpreted after the fact, when printing, that is actually matters (there may be other cases, as with comparison operators)



            -10 in 32BIT binary is FFFFFFF6
            42 IN 32bit BINARY is 0000002A


            Adding them together, it doesn't matter to the processor if they are signed or unsigned, the result is: 100000020. In 32bit, the 1 at the start will be placed in the overflow register, and in c++ is just disappears. You get 0x20 as the result, which is 32.



            In the first case, it is basically the same:



            -42 in 32BIT binary is FFFFFFD6
            10 IN 32bit binary is 0000000A


            Add those together and get FFFFFFE0



            FFFFFFE0 as a signed int is -32 (decimal). The calculation is correct! But, because it is being PRINTED as an unsigned, it shows up as 4294967264. It's about interpreting the result.






            share|improve this answer













            This is part of what is wonderful about 2's complement representation. The processor doesn't know or care if a number is signed or unsigned, the operations are the same. In both cases, the calculation is correct. It's only how the binary number is interpreted after the fact, when printing, that is actually matters (there may be other cases, as with comparison operators)



            -10 in 32BIT binary is FFFFFFF6
            42 IN 32bit BINARY is 0000002A


            Adding them together, it doesn't matter to the processor if they are signed or unsigned, the result is: 100000020. In 32bit, the 1 at the start will be placed in the overflow register, and in c++ is just disappears. You get 0x20 as the result, which is 32.



            In the first case, it is basically the same:



            -42 in 32BIT binary is FFFFFFD6
            10 IN 32bit binary is 0000000A


            Add those together and get FFFFFFE0



            FFFFFFE0 as a signed int is -32 (decimal). The calculation is correct! But, because it is being PRINTED as an unsigned, it shows up as 4294967264. It's about interpreting the result.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered 16 hours ago









            Garr GodfreyGarr Godfrey

            4,03711518




            4,03711518













            • As a side note, on x86, most arithmetic operations set various bits in the flags register, which can then be used to e.g. perform branching. For example, addition would set CF (carry flag) to signify that unsigned "wrapping" has occurred, and/or OF (overflow flag) to signify that signed overflow has occurred, if you were to consider the operands signed. There was even a 1-byte INTO instruction which generated a software interrupt if OF was set, which could help with debugging, but unfortunately was not supported from higher level languages, and is no longer available in 64 bit mode.

              – Arne Vogel
              4 hours ago






            • 1





              Technically speaking, nothing mandates 2's-complement representation. It's just that the modular-arithmetic conversion specified by the standard is exactly equivalent to using 2's-complement (as you'll see if you perform the conversion on a sign-magnitude or 1's-complement system, if it's correct).

              – Toby Speight
              3 hours ago



















            • As a side note, on x86, most arithmetic operations set various bits in the flags register, which can then be used to e.g. perform branching. For example, addition would set CF (carry flag) to signify that unsigned "wrapping" has occurred, and/or OF (overflow flag) to signify that signed overflow has occurred, if you were to consider the operands signed. There was even a 1-byte INTO instruction which generated a software interrupt if OF was set, which could help with debugging, but unfortunately was not supported from higher level languages, and is no longer available in 64 bit mode.

              – Arne Vogel
              4 hours ago






            • 1





              Technically speaking, nothing mandates 2's-complement representation. It's just that the modular-arithmetic conversion specified by the standard is exactly equivalent to using 2's-complement (as you'll see if you perform the conversion on a sign-magnitude or 1's-complement system, if it's correct).

              – Toby Speight
              3 hours ago

















            As a side note, on x86, most arithmetic operations set various bits in the flags register, which can then be used to e.g. perform branching. For example, addition would set CF (carry flag) to signify that unsigned "wrapping" has occurred, and/or OF (overflow flag) to signify that signed overflow has occurred, if you were to consider the operands signed. There was even a 1-byte INTO instruction which generated a software interrupt if OF was set, which could help with debugging, but unfortunately was not supported from higher level languages, and is no longer available in 64 bit mode.

            – Arne Vogel
            4 hours ago





            As a side note, on x86, most arithmetic operations set various bits in the flags register, which can then be used to e.g. perform branching. For example, addition would set CF (carry flag) to signify that unsigned "wrapping" has occurred, and/or OF (overflow flag) to signify that signed overflow has occurred, if you were to consider the operands signed. There was even a 1-byte INTO instruction which generated a software interrupt if OF was set, which could help with debugging, but unfortunately was not supported from higher level languages, and is no longer available in 64 bit mode.

            – Arne Vogel
            4 hours ago




            1




            1





            Technically speaking, nothing mandates 2's-complement representation. It's just that the modular-arithmetic conversion specified by the standard is exactly equivalent to using 2's-complement (as you'll see if you perform the conversion on a sign-magnitude or 1's-complement system, if it's correct).

            – Toby Speight
            3 hours ago





            Technically speaking, nothing mandates 2's-complement representation. It's just that the modular-arithmetic conversion specified by the standard is exactly equivalent to using 2's-complement (as you'll see if you perform the conversion on a sign-magnitude or 1's-complement system, if it's correct).

            – Toby Speight
            3 hours ago


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54190113%2fis-a-negative-integer-summed-with-a-greater-unsigned-integer-promoted-to-unsigne%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            GameSpot

            日野市

            Tu-95轟炸機