0

Why does this program output a negative value?

#include <stdio.h>

int main() {
    
    char a = 'a', b = 'b', c;
    
    c = a + b;
    
    printf("%d", c);

}

Shouldn't these values be converted into ASCII then added up?

3Dave
  • 28,657
  • 18
  • 88
  • 151
Ndm Bcc
  • 9
  • 1

3 Answers3

1

On the most common platforms, char is a signed type which can represent values from -127 to +128. You appear to be using a platform on which char has these properties.

Note that on some platforms, char is unsigned, so that the range of representable values is 0 to 255. However, you do not appear to be using such a platform.

Adding 'a' to 'b' is the same as adding their ASCII values (97 and 98 respectively), for a result of 195. This result is not representable as a char on your platform. In many implementations, the first bit is a sign bit, so you get -61.

Using unsigned char gives the result that you expect for printable 7-bit ASCII characters.

#include <stdio.h>

int main() {
    
    char a = 'a', b = 'b';
    unsigned char c;
    
    c = a + b;
    
    printf("%d\n",a);
    printf("%d\n",b);
    printf("%d\n", c);

}

Outputs:

97
98
195
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
3Dave
  • 28,657
  • 18
  • 88
  • 151
  • In your answer, you wrote: `"char is a signed type"` -- Although this statement is correct for the platform that OP is using, it is not necessarily correct for all platforms, as ISO C does not require this. [§6.2.5. ¶15 of the ISO C11 standard](http://port70.net/~nsz/c/c11/n1570.html#6.2.5p15) specifies that `char` must have the same range, representation and behavior as either `signed char` or `unsigned char`. Therefore, on some platforms, `char` is not a signed type. See this question for more information: [Is char signed or unsigned by default?](https://stackoverflow.com/q/2054939/12149471) – Andreas Wenzel Nov 29 '22 at 00:12
  • 1
    It is also not true that a signed `char` type “can represent values from -127 to +127.” That is the smallest interval the C standard requires. It permits more, and, −128 to +127 is overwhelmingly common. – Eric Postpischil Nov 29 '22 at 00:17
  • @AndreasWenzel I'd love a citation for a standards-compliant compiler, in production, that treats `char` as `unsigned` by default. While you might be technically correct, much like using `#once` for C++ header guards is not standards-compliant, it makes no difference in practice. – 3Dave Nov 30 '22 at 01:23
  • @EricPostpischil My range was incorrect. Thanks for pointing that out. – 3Dave Nov 30 '22 at 01:25
  • @3Dave: For example, according to [this answer](https://stackoverflow.com/a/2054959/12149471), gcc in Android NDK has `char` to be unsigned by default. – Andreas Wenzel Nov 30 '22 at 01:27
  • @AndreasWenzel Interesting! I feel that `char` should have been `unsigned` from the get-go. Maybe Google just decided to "fix" that issue. :) A negative character is semantically nonsensical. A signed byte makes more sense. – 3Dave Nov 30 '22 at 01:38
  • @EricPostpischil Speaking of standards, see the ISO `limits.h` contents here: http://port70.net/~nsz/c/c11/n1570.html#E . It specifically sets the range of `signed char` to `-127` to `+127`, implying distinct positive and negative zero. This tracks with the idea of a "sign bit." I have no doubt that some implementations offer a unique zero to allow for increased precision, but that's not indicated by the ISO. – 3Dave Nov 30 '22 at 01:47
  • 1
    @3Dave: I don't think that the range `-127` to `+128` is permitted. [§6.2.6.2 ¶2 of the ISO C11 standard](http://port70.net/~nsz/c/c11/n1570.html#6.2.6.2p2) allows the following 3 representations for signed numbers: (1) sign and magnitude, (2) two's complement, and (3) one's complement. I believe the upcoming C23 standard only allows two's complement. In none of these three representations would the range of representable values be `-127` to `+128`. – Andreas Wenzel Nov 30 '22 at 01:49
  • @AndreasWenzel I agree. I ammended my answer in response to EricPostpischil 's comment, and since amended the amendment. (All I was trying to do with this answer was say "big number + big number is more than 127." ) – 3Dave Nov 30 '22 at 01:52
  • @3Dave: In your answer, you wrote: `"or -127 to +128, or some other distribution of 256 unique values"` -- I believe that this part is wrong, for the reason stated in my previous comment. Well, at least the `"or -127 to +128"` part is wrong. – Andreas Wenzel Nov 30 '22 at 01:55
  • @AndreasWenzel Fixed - to a version that is what I initially said but with incorrect redactions subsequently redacted. I give up. – 3Dave Nov 30 '22 at 02:04
  • The fact the C standard specifies the range of `signed char` must be −127 to +127 allows for the option of a “negative zero.” It does not imply there is a negative zero. – Eric Postpischil Nov 30 '22 at 02:15
  • I liked [revision 3](https://stackoverflow.com/revisions/74607716/3) of your answer best, before you started talking about the details of the standard. In my opinion, the only change required is to change the first sentence of that revision to the following: "On most common platforms, `char` is a signed type which can represent values from `-128` to `+127`. You appear to be using a platform on which `char` has these properties." – Andreas Wenzel Nov 30 '22 at 02:47
  • @AndreasWenzel ISO specifies `char` ranges from -127 to +127. – 3Dave Nov 30 '22 at 04:40
  • @EricPostpischil I'll not argue "allow" vs "imply." It's not really germane to OP's question. I'm tempted to just nuke the answer and move on with my life. – 3Dave Nov 30 '22 at 04:41
  • @3Dave: Yes, that is the minimum range specified by the ISO C11/18 standard. On most common platforms, the actual range is `-127` to `+128`, due to using two's complement representation. If a platform does not use that representation, but uses sign and magnitude or one's complement representation instead, then the actual range would be `-127` to `+127`. But the [upcoming ISO C23 standard](https://open-std.org/JTC1/SC22/WG14/www/docs/n3054.pdf) requires two's complement representation and forbids the other two types of representation, so it defines the minimum range to be `-127` to `+128`. – Andreas Wenzel Nov 30 '22 at 14:11
  • @3Dave: Since you appear to be growing frustrated with fixing the answer and have stated that you are considering deleting it for this reason, I have edited your answer for you. If you do not like my changes, feel free to revert them. I have deleted the part in which you refer to the ISO C standard, as this information will be outdated in ISO C23. – Andreas Wenzel Nov 30 '22 at 14:35
1
a   = 97
b   = 98
a+b = 195

195 is out of the signed 8-bit range (-128 ... 127)

195 = 0b11000011

This equals -61 in signed 8-bit representation.

Karrer
  • 44
  • 5
  • 1
    There is no overflow. Overflow is when the defined result of the operation is not representable in the result type. In `a + b`, `a` and `b` are converted to the `int` type, and the addition produces a value representable in that type. In `c = …`, the assignment is defined to convert the right operand to the destination type, `char`. ). With a signed `char`, the conversion is implementation-defined, so it produces a representable result. The result is always representable, not overflowed. For OP’s implementation, that is a wrapped defined result, not an overflow. – Eric Postpischil Nov 30 '22 at 02:19
  • You may want to read about [integer promotion](https://en.cppreference.com/w/c/language/conversion#Usual_arithmetic_conversions). – Andreas Wenzel Nov 30 '22 at 02:42
0

As explained by 3Dave char is a signed type and adding up two variables of such type can lead to overflow and it produces a negative result. Even if you use unsigned char this sum can result in an overflow, but not with negative value.

  • 1
    There is no overflow in `c = a + b;`. Overflow is when the defined result of the operation is not representable in the result type. In `a + b`, `a` and `b` are converted to the `int` type, and the addition produces a value representable in that type. In `c = …`, the assignment is defined to convert the right operand to the destination type, `char`. If `char` is unsigned, that is defined to wrap modulo 256 (in the usual 8-bit `char` type). If it is signed, the conversion is implementation-defined, so it produces a representable result. The result is always representable, not overflowed. – Eric Postpischil Nov 29 '22 at 00:15
  • You may want to read about [integer promotion](https://en.cppreference.com/w/c/language/conversion#Usual_arithmetic_conversions). – Andreas Wenzel Nov 29 '22 at 00:51
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 01 '22 at 10:18