0

I'm learning about converting numbers to characters in C, and I came across the expression:

char c = '0' + (n % 10);

I understand that '0' is a character and n % 10 extracts the last digit of a number, but I want to understand in detail:

Why do we add '0' to (n % 10)?

How does this relate to the ASCII table?

Why is '0' represented as 48 in ASCII, and how does adding a digit to it produce the correct character?

How can this be used to print numbers as characters?

Could someone please explain this concept step-by-step with examples? Also, how does the modulo % operator help in extracting digits from a number?

I tried printing the character values of digits by adding '0' to them, like this:

int digit = 5;
char c = '0' + digit;
printf("%c\n", c);  // Expected output: '5'

I expected this to print the character '5', and it worked. However, I want to understand the underlying reason why adding '0' converts a digit to its character form, and how this ties into ASCII values and modulo operations.

9
  • '0' is character. That is an integer. So, '0' is an alias of 48 So '0'+5 is 48+5 that is 53 that is '5' Commented Jul 11 at 18:57
  • As for why '0' is 48 rather than 100 or 12. Well, its a code. You have to assign a value to each byte. What matters in your case, is that ASCII codes of 0, 1, 2, 3, ..., 9 are consecutive (I would assume that when ASCII code was defined, choosing a quite round number for ASCII code of '0' (48 is 110000 in binary) made it easier to compute ascii code of numbers (just have to 110000 OR digit) Commented Jul 11 at 19:01
  • And for the modulo question... that is another question. Rule here is "1 question=1 post". You'll have to edit it out of this one, and ask another question. Commented Jul 11 at 19:01
  • 1
    The char type is an integer type, it's the smallest integer type with size of 1 byte (= 8 bits), so it can only store integer numbers. When you attribute a character to a char variable, such as in char c = '0';, that character is converted to its respective integer number in a character table (such as the ASCII table), and that number is then stored in the variable. And when you want to print it as a character the opposite conversion is made. Commented Jul 11 at 19:04
  • Possible duplicate : stackoverflow.com/questions/30029294/… Commented Jul 11 at 19:04

7 Answers 7

7
  1. C standard in 5.2.1 3 guaranteed that character representation of decimal digits is contiguous:

    In both the source and execution basic character sets, the value of each character after 0 in the above list of decimal digits shall be one greater than the value of the previous.

  2. char type is an integer and it holds a number.

  3. '0' , '1' etc are character constants and have int type and value of the representation of the mapped character interpreted as an integer

  4. So if the integer representation of '0' is 48 then representation of '1' will be 49. It does not matter what encoding system computer uses (ASCII or something else).

  5. So '0' + <0,9> will provide integer representation of a digit.

Sie note:

Why do we add '0' to (n % 10)?

(n % 10) my produce negative number if n < 0 so safer version is abs(n % 10)

Sign up to request clarification or add additional context in comments.

3 Comments

"'0' + <0,9> will provide integer representation of a digit." is true, yet , depending of the range/type of n, n%10 may produce a negative remainder.
chucks - where do you see n in my answer?
With '0' + <0,9>, the <0,9>, if related to OP's question, derives from n % 10.
5

In the ASCII table the characters '0' through '9' have consecutive values, i.e. 48 to 57. This means that adding a value between 0 and 9 to '0' results in the corresponding ASCII character for that digit.

Character Value 48+n '0'+n
'0' 48 48 + 0 '0' + 0
'1' 49 48 + 1 '0' + 1
'1' 50 48 + 2 '0' + 2
'3' 51 48 + 3 '0' + 3
'4' 52 48 + 4 '0' + 4
'5' 53 48 + 5 '0' + 5
'6' 54 48 + 6 '0' + 6
'7' 55 48 + 7 '0' + 7
'8' 56 48 + 8 '0' + 8
'9' 57 48 + 9 '0' + 9

This works even for encodings other than ASCII as the C standard guarantees that the values for the decimal digit characters are consecutive.

1 Comment

It also works in EBCDIC. Range is 0xF0 to 0xF9
3

First off, this trick only works for charsets which have digits 0..9 represented as sequential characters. ASCII is such a charset (as as other ASCII-compatible charsets).

In any given charset, each character has a specific numeric value assigned to it. For example, ASCII characters '0'..'9' have numeric values 48..57, whereas EBCDIC characters '0'..'9' have numeric values 240..249, etc.

Human-readable numbers are typically represented in base 10. So, given any integer n, taking the modulo 10 (n % 10) will return the last digit in base 10. For example, 105 % 10 returns 5, as modulo is the remainder after division, eg 105 / 10 is 10 with a remainder of 5.

The modulo in base 10 will give you integer values in the range 0..9. So, adding them to ASCII '0' will give you characters 48+0..48+9 = 48..57, and adding them to EBCDIC '0' will give you characters 240+0..240+9 = 240..249, etc.

2 Comments

First off, this trick only works for charsets which have digits 0..9 represented as sequential characters. ASCII is such a charset (as as other ASCII-compatible charsets). The C Standard mandates that the characters 0 through 9 be consecutive in the execution character set, so this remark is misleading.
Also there has never been an actual character set in anything like widespread use that did not have this property. Even EBCDIC, with its weirdly noncontiguous alphabetic letters, had perfectly contiguous digits, so code like that in the question would work there, too.
2

I understand that '0' is a character…

No, it is not. '0' is a number. It is the number that is the code for the character “0”. Whenever the compiler sees 'x', it translates it to the number that is the code for the character “x” in the execution character set.

Most current C implementations use ASCII, but, even if they do not, the C standard requires the codes for the digits “0” to “9” be consecutive integers. Therefore, if you add a value from 0 to 9 to the code for “0”, you will get the code for the corresponding digit.

When you print this number with printf and %c or with putchar, it causes the character corresponding to the code to be printed.

Also, how does the modulo % operator help in extracting digits from a number?

In a decimal numeral abcd, the number represented by the numeral is 1000a + 100b + 10c + d. Clearly the remainder of 1000a + 100b + 10c + d when divided by ten is d. So x % 10 will give the last digit of the decimal numeral for x. (Except, if x is negative, we would need to make adjustments, because then the % operator does not produce a positive remainder.)

Comments

2

How does '0' + (n % 10) convert an integer digit to its character representation in C?

This works for a single integer digit, though it does not always work for all integer values. It fails when n < 0.

Watch out for signed math

When n is an int, long, char (when char is signed) or some other signed integer type, n may be less than 0. n % 10 returns a value in the -9 ... +9 range, not only 0...9.

Either make certain n >= 0 or consider unsigned math.

Comments

2

It might help to step away from ASCII for a moment and consider the much more sensible (albeit nonstandard) SCSCII, the Steven Charles Summit Code for Information Interchange. In SCSCII, the digits have the following values (in decimal):

'0'    37
'1'    38
'2'    39
'3'    40
'4'    41
'5'    42
'6'    43
'7'    44
'8'    45
'9'    46

So if you have the digit 0 and you want to convert it to the character '0', you have to add 37, because 0 + 37 = 37 = '0'.

Similarly, if you have the digit 3 and you want to convert it to the character '3', you have to add 37, because 3 + 37 = 40 = '3'.

And in fact, for any digit d, you can convert it to its corresponding character by adding 37, because the digit characters 0..9 have consecutive values 37..46, so there's a constant offset, 37.

And since 0 + 37 = 37 = '0', that magic offset 37 Just Happens To Be the same as the numeric value of the character '0', which is 37.

So if you have a digit d and you want to convert it to the corresponding character, and if for some reason you don't remember that in SCSCII the value of the character '0' is 37, you don't have to write d + 37, you can cheat and write d + '0'. Except, this isn't really "cheating", it's really just "taking a shortcut". And in fact, as we're about to see, it's not even "taking a shortcut", in fact it's really "being totally reasonable".

I was joking about the "much more sensible SCSCII character set", of course. But suppose we went through the same analysis for the much more standard ASCII, in which '0' is 48 and '1' is 49, up to '9' is 57. In ASCII, the magic offset is 48, so to convert a digit to a character you'd have to write d + 48.

...Or, well, you could write d + 48, but if you didn't feel like remembering that the offset for ASCII was 48, you could apply the same shortcut as above and write d + '0' instead.

Which is exactly the same shortcut we used for SCSCII! How can that work? How can the code d + '0' properly convert digits to characters on a SCSCII machine, while the exact same code d + '0' properly converts digits to characters on an ASCII machine?

Well, it works because on a SCSCII machine, that character constant '0' has the value 37, while on an ASCII machine, the character constant '0' has the value 48. It's as if you had written

#define DIGIT_TO_CHARACTER_OFFSET 37

...

... d + DIGIT_TO_CHARACTER_OFFSET ...

on the SCSCII machine, with the intention of changing it to

#define DIGIT_TO_CHARACTER_OFFSET 48

when you moved your code to an ASCII machine. Except, you don't have to muck around defining your own DIGIT_TO_CHARACTER_OFFSET macro, because the plain old character constant '0' always has just the right value, automatically, so it can perform precisely the same function for you.


You also asked:

How can this be used to print numbers as characters?

If you're not getting it, try this simple code:

printf("'0' has code %d\n", '0');
printf("48 is character %c\n", 48);

This code might bother you a bit. The first line uses %d to print a character, and you might have thought that %d was only for printing integers. Contrariwise, the second line uses %c to print an integer, and you might have thought %c was only for printing characters. But try it, and see what you get.

In fact, in C, characters are just tiny integers, having as their value the code for a character in the machine's character set. So there's nothing wrong with using %c to print an integer, or %d to print a character, because you're basically doing both of those things all the time, whether you realize it or not.


You also asked,

How does the modulo % operator help in extracting digits from a number?

Suppose you have the number 123, and you want to extract the last decimal digit. Now, 123 ÷ 10 is 12, remainder 3. Or, in C, 123 / 10 is 12, while 123 % 10 is 3. So, dividing modulo 10, or % 10, is how you extract the last base-10 digit of an integer in C (or just about any language).


You also asked,

Why is '0' represented as 48 in ASCII?

There's not really a reason — in the end it's basically arbitrary. You can read about the history on Wikipedia. They could have picked 240 like EBCDIC did, or 37 like SCSCII did, or any other value. The virtue of the ASCII character — the ASCII Standard character set — is not that the value 48 for the character '0' means anything. The virtue is simply that it's a standard that we all agree on. So if I transmit the character codes 72 101 108 108 111 44 32 119 111 114 108 100 33 to you (or my screen, or my printer, or whatever) we all agree that those codes represent exactly the string "Hello, world!", and not anything else.

But no matter what value the character '0' has in the character set you're using today, that magic code d + '0' will work, as long as all ten digits have consecutive codes.

Comments

0

A rough summary of the design choices of ascii is for convenience in terms of representing character symbols and the shift operator/key.

The letters are spaced 32 apart, such that you can lowercase/upppercase a letter by setting or unsetting the 5th bit (2^5). This is what your shift key essentially does for you. The numbers are all in a row such that subtracting the representation for 0 will transform the digit into its value '0' - '0' -> 0, '1' - '0' -> 1 etc...

A number of special control characters were reserved in the range 0-32 (32 being the first printable character ' ' a blank space!), they include some hold overs from when type writers were a thing. For example there's a representation for carriage return, a control code representing resetting the typehead back to the start of the line '\r'. This is why there's some modern day confusion over representation of line breaks in software, because \r\n, roughly represents how a typewriter would move to a new line (first carriage return, then rotate the cylinder to the new line) which a lot of older software adopted as the standard. Where most software developers realized \n is sufficient for software to represent the whole operation.

Now as for the code. char, short, int, long are all integer types. So the explanation of why you're adding the character '0' to n%10 is that your compiler has the value of the character code for 0 built in via the literal '0'. And so, incorporating the knowledge of ascii representation for numbers all we need to do is add the value of the digit to get the correct character code. Hence char c = '0' + (n % 10);

The % is the remainder / modulus operator, % 10, essentially reads divide n by ten and return the remainder. This is what gets the digit value, for example if we wanted to print the 6 in 56 we could do 56 % 10. So now you have all the pieces together.

2 Comments

" add the value of the digit to get the correct character code", '0' + (n % 10) is insufficient when n < 0.
Sure, the question though was to make sense / explain what someone else was doing, so I didn't bother with explaining input validation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.