13

According to the rules on implicit conversions between signed and unsigned integer types, discussed here and here, when summing an unsigned int with a int, the signed int is first converted to an unsigned int.

Consider, e.g., the following minimal program

#include <iostream>

int main()
{
   unsigned int n = 2;
   int x = -1;

   std::cout << n + x << std::endl;

   return 0;
}

The output of the program is, nevertheless, 1 as expected: x is converted first to an unsigned int, and the sum with n leads to an integer overflow, giving the "right" answer.

In a code like the previous one, if I know for sure that n + x is positive, can I assume that the sum of unsigned int n and int x gives the expected value?

francesco
  • 7,189
  • 7
  • 22
  • 49
  • 1
    Are you compiling with all warnings enabled? – JGroven Nov 06 '18 at 22:44
  • 2
    @JGroven how will that impact the answer? – Fureeish Nov 06 '18 at 22:45
  • Possible duplicate? https://stackoverflow.com/questions/17832815/c-implicit-conversion-signed-unsigned – asu Nov 06 '18 at 22:45
  • @Fureeish, if the compiler warns you about what you're doing, you probably shouldn't be doing it, which would mean that the assumption that the expected value is correct would be false. – JGroven Nov 06 '18 at 22:46
  • 1
    Well, *probably*, *would* and so on are **not** guarantees. The fact that compiler may warn about *possible problems* does not mean that, given a specific criteria, like "*if I know for sure that `n + x` is positive*", it will, or even it *could* fail – Fureeish Nov 06 '18 at 22:47
  • @JGroven I know that the compiler issues a warning when ```Wsign-conversion``` (gcc or clang) is enabled. Still I would like to understand if this warning is a serious issue or not. – francesco Nov 06 '18 at 22:49
  • This would depend I think on the standard guaranteeing the conversion to unsigned to be in 2's complement format which kind of rings a bell... – Galik Nov 06 '18 at 22:49
  • 2
    @JGroven "*if the compiler warns you about what you're doing, you probably shouldn't be doing it*." Err it's a "warning" for a reason--to bring it to your attention, just in case. There are many cases I've run into where I have `WARNING: Unused variable X` in production code that are actually fine (maybe because the declaration alone had an effect or the compiler is just **wrong**). – scohe001 Nov 06 '18 at 22:49
  • @Asu I am aware of the discussion you link and in fact I have mentioned and linked it in the question. – francesco Nov 06 '18 at 22:50
  • 2
    This will be implementation defined. The standard says the bit patterns for signed and unsigned will be identical but 2's complement is not guaranteed. So it would be implementation defined if this would work or not. I believe that `std::int8/16/32_t` are guaranteed to be 2's complement so they would always work. – Galik Nov 06 '18 at 22:55
  • @francesco Oh, my bad. I misunderstood the point of the question. – asu Nov 06 '18 at 23:00
  • @Galik but unsigned integers follow well defined modular arithmetic, as does conversion from signed to unsigned, so it isn't clear to me what can be implementation defined, if the int gets converted to `unsigned int` before the operation. – juanchopanza Nov 06 '18 at 23:00
  • @juanchopanza Modular arithmetic when adding `1` to `-1` bit patterns only works between signed and unsigned **if** the bit pattern uses 2's complement notation. Other formats are allowable by the standard. – Galik Nov 06 '18 at 23:08
  • @Galikbut the `int` gets converted to unsigned. So 2's complement doesn't come into it. – juanchopanza Nov 06 '18 at 23:12
  • @juanchopanza Well if the `int` is using 1's complement the resulting converted `unsigned` value will be different compared to if the `int` is using 2's complement. So adding `1` to it will yeild a different result also. – Galik Nov 06 '18 at 23:15
  • @Galik No I don't think so. The standard defines that conversion quite well IIRC. – juanchopanza Nov 06 '18 at 23:16
  • @juanchopanza Well the standard says that each `unsigned` integer type has the same *object representation* as its corresponding `unsigned` type. So the bit patterns will be identical. – Galik Nov 06 '18 at 23:20
  • @juanchopanza On balance I think you are probably right. It just seems that two parts of the standard appear to be contradicting each other (to my reading). But I am not entirely convinced I have correctly interpreted the legalese. – Galik Nov 06 '18 at 23:45
  • @Galik: 7.8(2) indirectly defines that an actual conversion is applied for systems other than 2s complement. It's not completely clear, but I'd tend to follow juanchopanza's arguments. – Stephan Lechner Nov 06 '18 at 23:45
  • 1
    @Galik: In my opinion the representation doesn't matter. The standard is clear. The result will be a congruent number modulo 2^n. Which is the contradicting part of the standard you mentioned? – geza Nov 06 '18 at 23:47
  • @geza I am looking at `§ 6.9.1` part `3` where it says "*each signed integer type has the same object representation as its corresponding unsigned integer type*". – Galik Nov 06 '18 at 23:49
  • @geza Also *"a congruent number modulo 2^n"* could mean the notional integer value according to real world math (probably this) **or** is it talking about the bit pattern **after** assuming the equivalent object representation format corresponding unsigned type? Meaning non 2's complement types would not wrap round in the same place during modulo arithmetic of the represented value? – Galik Nov 06 '18 at 23:56
  • @Galik: I still don't think that repr. matters here. I think that "the same object representation" basically means that they have the same size. It doesn't say anything about the value. When the standard says about modulo arithmetic, it means the value. Nowhere said that the bit pattern matters. Even, how could one interpret it, if it would mean the bit pattern? You cannot do a modulo on a bit pattern. You need to get the value first which it represents. – geza Nov 07 '18 at 00:16
  • @Galik arithmetic is defined in terms of values, not representations. – M.M Nov 07 '18 at 00:19

2 Answers2

5

In a code like the previous one, if I know for sure that n + x is positive, can I assume that the sum of unsigned int n and int x gives the expected value?

Yes.

First, the signed value converted to unsigned, using modulo arithmetic:

If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type).

Then two unsigned values will be added using modulo arithmetic:

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.

This means that you'll get the expected answer.

Even, if the result would be negative in the mathematical sense, the result in C++ would be a number which is modulo-equal to the negative number.

Note that I've supposed here that you add two same-sized integers.

geza
  • 28,403
  • 6
  • 61
  • 135
1

I think you can be sure and it is not implementation defined, although this statement requires some interpretations of the standard when it comes to systems that do not use two's complement for representing negative values.

First, let's state the things that are clear: unsigned integrals do not overflow but take on a modulo 2^nrOfBits-value (cf this online C++ standard draft):

6.7.1 Fundamental types

(7) 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.

So it's just a matter of whether a negative value nv is converted correctly into an unsigned integral bit pattern nv(conv) such that x + nv(conv) will always be the same as x - nv. For the case of a system using two's complement, things are clear, since the two's complement is actually designed such that this arithmetic works immediately.

For systems using other representations of negative values, we'll have to read the standard carefully:

7.8 Integral conversions

(2) If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is notruncation). —endnote]

As the footnote explicitly says, that in a two's complement representation, there is no change in the bit pattern, we may assume that in systems other than 2s complement a real conversion will take place such that x + nv(conv) == x - nv.

So due to 7.8 (2), I'd say that your assumption is valid.

Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
  • 1
    Arithmetic is defined in terms of values, not representations. It does not matter at all whether the system uses 2's complement or not. `2 + -1` must give `1` regardless. – M.M Nov 07 '18 at 00:09
  • @M.M: yes, that's why the result is not implementation dependant, although it was argued elsewhere that it might only work in case of 2s complement. – Stephan Lechner Nov 07 '18 at 06:10