Classroom practice: Arithmetic types

Gábor Horváth / Zsolt Kohári · 2020.10.09.

Working with characters, character coding. Number systems, conversion between number systems.

1. Small letters, capital letters

Let us write a program that reads a text from the standard input, converts it to lowercase letters (or capital letters) and prints it to the screen! Characters other than letters should be left unchanged. First solve the task by converting the cases manually, then solve it by using the related built-in functions!

Solution

The characters are just numbers for the computer. Consequently, adding and subtracting two characters (based on the underlying character codes) is a valid operation. In C, the ASCII table is used for the character encoding, where the small case letters follow each other in lexicographic ordering, as well the upper case letters, they just start at a different position. Hence, the expression 'C'-'A' yields 2, since the distance between letter C and letter A is 2. This is the key to the solution: if we subtract the code of 'A' from the code of a capital letter, we get which letter it is in the alphabet. Then, adding the code of 'a' to it we get the corresponding small case letter, and vice versa. Putting it differently: the 'a'-'A' expression is the difference between the locations of the small case and upper case letters in the ASCII table, which has to be added to of sutracted from a character to convert upper case to lower case, or vice versa, accordingly. It is important to note that we dont have to know the concrete value of the character codes. Actually, we should never write specific character codes in a C code, only characters in aphostrophs.

From small case to upper case:

#include <stdio.h>

int main(void) {
    char cin, cout;
    while (scanf("%c", &cin) == 1) {
        if (cin >= 'a' && cin <= 'z') {
            cout = cin - 'a' + 'A';
        } else {
            cout = cin;
        }
        printf("%c", cout);
    }

    return 0;
}

The program reads the input till the end-of-file symbol, thus to terminate the program press F6Enter (in Windows), or Ctrl+D (in Linux). The same task solved with the toupper() function of ctype.h is as follows:

#include <stdio.h>
#include <ctype.h>

int main(void) {
    char cin;
    while (scanf("%c", &cin) == 1) {
        printf("%c", toupper(cin));
    }

    return 0;
}

2. Number systems

hexbindec
000000
100011
200102
E111014
F111115

Let us convert the following base-2 (binary) numbers to base-10 (decimal) numbers, on paper: 1011, 1101, 10000, 10101! Let us convert the following decimal numbers to binary numbers: 13, 27, 35.

Let us convert the following base-16 (hexadecimal) numbers to binary numbers: 0x1F, 0xFCE2, 0xABBA, 0xC0FFEE! Let us convert the following binary numbers to hexadecimal: 11111111, 1011100, 101, 10101! During the conversions do not use the decimal system as an intermediate step!

Hint

24=16, thus every hexadecimal digit can be represented by exactly 4 binary digits, as shown on the right. Therefore, the hexadecimal value of binary number 101011 is 0x2B, since the lower 4 bits (starting from the right) are 1011 (=0xB), and for the upper bits we have 10=0x2.

Let us do the calculations below in the binary system, without converting the terms to decimal!

 1001
+ 101
─────
 1111
+1000
─────
 1111
+   1
─────

3. Reading numbers in a given number system

Let us read a number from the user, in the number system specified by the user!

First get it work with the decimal system, then generalize to arbitrary base!

Which number system to use?
16
Enter the number!
fce2
In decimal system the number is: 64738

Solution

Solution plan, hints

  • The digits can be read one-by-one as characters, subtracting '0' from it we get the value of the digit.
  • How to make it work with longer numbers rather than single digits? For instance, in case of 123, if we have 12 already read, and then a digit 3 is observed, the 12 must be multiplied by 10, and add 3 to it. If a digit 4 arrives after having 123 read, we multiply it by 10 and add 4 to it.
  • Consequently, we always need to take the number read so far, multiply it by 10, and add the new digit to it.

To solve the task we can not use the capabilities of scanf(), since it supports automatic number system conversion only for bases 10, 16 (%x) and 8 (%o). We have to read the digits one by one and assemble the number as described above.

When reading digits we have to keep in mind that above base 10 the digits can be letters, too. Thus, every character has to be checked if it is a numberic digit (0...9) or an alphabetic character (a...z). If it is a numeric digit, the code of '0' has to be subtracted from it to get its value; otherwise the code of 'a' has to be subtracted and 10 must be added to get the value (since A=10, B=11 etc.) To make the solution simpler, we can use some useful functions of ctype.h: isdigit(c) = is it a digit?, isalpha(c) = is it an alphabetical character?, toupper(c) = the character converted to upper case.

#include <stdio.h>
#include <ctype.h>

int main(void) {
    /* base */
    int base;
    printf("Which number system to use?\n");
    scanf("%d", &base);

    /* reading the number */
    printf("Enter the number!\n");
    int number = 0;
    char c;
    /* The following scanf eats the "enter" character pressed after reading the base above.
     * Otherwise the scanf %c of the loop would give it back as well */
    scanf(" ");
    while (scanf("%c", &c) == 1 && !isspace(c)) {
        /* converting the character to a numeric value */
        int value;
        if (isdigit(c))
            value = c - '0';
        if (isalpha(c))
            value = toupper(c) - 'A' + 10;
        /* update the number */
        number = number*base + value;
    }

    printf("In decimal system the number is: %d\n", number);

    return 0;
}

4. Printing numbers in a given number system

Let us write a program that prints a number in a given number system!

Let us solve the task both with and without arrays!

Hints

  • Dividing the number by the base of the number system and taking the remainder gives us the last digit. For instance, 1234 % 10 = 4, the number ends with digit 4.
  • If we take the result of the division instead, we get the remaining part of the number (to be printed out): 1234 / 10 = 123.
  • Doing these steps iteratively we get all the numeric digits to print, with one flaw: in a reverse order!

Solution

Version 1.

Determining the digits is easy, see the steps above:

This is not correct yet
#include <stdio.h>

int main(void) {
    int base, number;
    
    printf("Which number system to use?
");
    scanf("%d", &base);
    printf("The (decimal) number is:
");
    scanf("%d", &number);

    while (number > 0) {
        printf("%c", (number%base) + '0');   // reverse order :(
        number = number / base;
    }
    return 0;
}

The problem with this solution is that it prints the least significant digit first... while it should start printing the most significant one:

Which number system to use?
10
The (decimal) number is:
123
The number in the given system is:
321

Version 2.

The simplest way to reverse the order of the digits is to use an array. Instead of printing them, the digits are stored to an array (as characters), and the program prints the content of the array in a reverse order:

A correct solution
with array
#include <stdio.h>

int main(void) {
    int base, number;
    
    printf("Which number system to use?\n");
    scanf("%d", &base);
    printf("The (decimal) number is:\n");
    scanf("%d", &number);

    /* obtaining the digits and storing them in an array */
    char digits[32];
    int len = 0;
    while (number > 0) {
        digits[len++] = number%base + '0';
        number /= base;
    }

    /* printing the array in a reverse order */
    for (int i = len-1; i >= 0; i=i-1)
        printf("%c", digits[i]);
    
    return 0;
}

Without arrays we need a completely different solution. A relatively short solution can be obtained if the program looks for the smallest power of the base which is greater than the number to convert, and from this power on we proceed backwards. For instance, if the number to print is 1234 (decimal), then dividing it by 1000, by 100, by 10 and by 1 we get numbers that end with the digit to be printed next to the screen, hence with (1, 12, 123, 1234).

#include <stdio.h>

int main(void) {
    int base, number;
    printf("Which number system to use?\n");
    scanf("%d", &base);
    printf("The (decimal) number is:\n");
    scanf("%d", &number);

    /* looking for the exponent that is bigger than the number to convert */
    int exponent = 1;
    while (exponent <= number)
        exponent *= base;
    
    /* go back by one, and proceed backwards */
    exponent /= base;
    while (exponent > 0) {
        printf("%d", (number/exponent) % base);
        exponent /= base;
    }
    
    return 0;
}

This solution still has as issue, it does not print anything when zero is entered, since the body of the loop does not get executed at all.

The solution without the array has a significant advantage: it works with any large numbers. In case of arrays we had to specify the length of the array right at the beginning, which poses a restriction on the biggest possible input.

Let us modify the program to get it work with number systems with base greater than 10! (For instance, with base 16 numbers). The digit 10 and the values greater than that are denoted by letters in this case. In base 16 system the 0…15 digits are: 012…89ABCDEF.

Solution

Only the printing part has to be modified, an "if" must be added to check whether we need to print a digit or an alphabetic letter.

if (number == 0)
    printf("0");
else {
    /* go back by one, and proceed backwards */
    exponent /= base;
    while (exponent > 0) {
        int value = (number/exponent) % base;
        if (value >= 10)
            printf("%c",  value-10+'A');
        else
            printf("%c",  value+'0');
        exponent /= base;
    }
}