In previous posts we’ve seenhow to perform operations with numbers, and we also know how to print decimal numbers on the screen.
During the next two posts we’re going to learn what numeric types MSX-C supports, how we (the programmers) can represent numbers in our programs, and how to print numbers in different notations.
Let’s dig in:
Four different notations
Let’s see what they’re useful for:
This doesn’t need much explanation. It’s the kind of number we use all the time. We’ll use this representation very often as counters in loops, as coordinates or color numbers when putting things on the screen, etc.
Be careful not to put a zero to the left of a decimal number. Using zero as a prefix means that the number is written in octal notation! As an example, in a C program “20” (decimal) means just 20, while “020” (octal) means 16:
Numbers written in decimal can be either of type int or unsigned. We’ll learn about this in the next post.
This notation is useful when we’re working with hardware devices or handling data organized in bit patterns (such as graphics). Think of this as a notation to do technical work.
In C we write hexadecimal numbers with the prefix “0x” (a zero and a lower case letter ‘x’), followed by a mix of the numbers 0-9 and letters A-F. The letters A-F can be either lower case or upper case, but the ‘x’ in the prefix must always be lower case.
In C we write octal numbers with a leading zero, followed by one or more digits in the range 0-7. It’s easy to misread these as if they were decimal, so always pay attention when you see a leading zero.
This notation was very popular in the 70s and 80s. It was the technical notation back then because computers of the time such as the PDP-8, the ICT 1900 and IBM mainframes were able to handle data sizes of 12, 24 or 36 bits, which are all divisible by three. An octal digit can be represented in three bits, making octal the ideal abbreviation of binary. Modern computer architectures use data sizes of 8, 16, 32 or 64 bits (all divisible by 4), so nowadays it has been mostly replaced with hexadecimal, but it’s still used for things such as UNIX file permissions.
Character constants consist on a pair of single quotes enclosing one character. You may think that it’s weird to use characters as numbers, but from MSX-C’s point of view a character constant is equivalent to the character’s ASCII code. For example, the character constant ‘A’ is equal to 65, ‘B’ equals 66, and so on.
As an example, the program below uses all four notations: decimal (in the output), hexadecimal, octal and a character constant. Note how besides printing the values, we can also perform arithmetic operations (in this case a sum) regardless of the notation used.
Note also how we’ve written the third printf():
In standard C we could omit the “(int)” before the character constant, but MSX-C is very sensitive about data types in functions. In this case, the %d parameter in printf() expects to find an int value to print, but character constants are always of type char. The “(int)” in front of the character constant tells the compiler to handle the value as if it was an int. This is what we call a cast. We’ll see in a moment why we need it.
Displaying numbers in different notations
In all the programs we’ve seen so far we’ve been printing numbers in decimal notation only. The example program shown before also prints decimal numbers, even though the source code has numbers in several notations. These notations are only a convenience for the programmer, and the compiler converts all these numbers to binary during the compilation process.
Printing numbers in decimals is just one of the many things that printf() can do. It is up to us to decide what notation we want to use. So far we’ve been using the code “%d” to print decimal numbers, but there are more of these codes. printf() can display numbers in five notations: decimal, hexadecimal, octal, characters, and unsigned decimal.
These are the five codes supported. We call them format specifiers:
This is what they do:
Display a number in decimal notation: %d
We’ve seen this format specifier already. It prints a decimal number between -32768 and 32767. Do not mix up with %u, which also prints a decimal number, but unsigned.
Display a number in hexadecimal notation: %x
Displays a number in hexadecimal notation. Be aware that this format specifier doesn’t print the “0x” before the hexadecimal digits. Also, the digits A-F are always printed uppercase.
Display a number in octal notation: %o
%o displays a number in octal notation. As with %x, this specifier doesn’t print a leading 0 before the octal digits.
Display a number as a character: %c
This format specifier works almost in the same way as the function putchar() that we’ve already seen: it prints the character whose character code matches the number passed to printf() as a parameter. However, while putchar() takes a char parameter, printf()’s %c specifier uses a number of type int or unsigned.
Character numbers go from 0 to 255, so if we try to display a character outside this range the results will be unspecified.
Display a number in unsigned decimal notation: %u
Like %d, %u also displays a decimal number, but with a different range. The range in this case is from 0 to 65535. This has to do with the way MSX-C handles int and unsigned values. We’ll see more about this in a future post.
The program below displays the numbers 64 through 80 in the five notations we’ve seen in this post:
Compile and run it and you’ll see that it prints numbers 64 to 80 (inclusive) in all five notations side by side: decimal, hexadecimal, octal, character and unsigned decimal (which in this case happens to be the same as signed decimal):
This time we’ve learnt how to write numbers in different notations inside our programs: decimal, hexadecimal, octal and character constants. We’ve also learn a few format specifiers to display these on the screen using the printf() library function.
In the next post…
Get ready for something more technical. In the next post we’ll look at the MSX-C numeric types in much more detail. We’ll learn what they are, what ranges they support, how to use them, and something very, very important: how they are stored inside the memory of our MSX computers.