Thursday, January 10, 2008

Top Ten, 16 of 10

When we last left our hero, we were looking at ten ways to get screwed by the "C" programming language. Today's entry is Octal numbers.

In C, the list 007, 008, 009, 010 is interpeted in octal, not decimal, with 010 equal to 8 decimal. Fortunately, the compiler emits an error for 008 and 009, as they have non-octal digits. This is not a warning. No object code is produced.

If you are unlucky enough not to have included an 8 or a 9 in your list of numbers, there is no warning. You just don't get the numbers you might have expected. If you want to align numbers in your code virtically, use spaces.

I confess that these days, i write most of my binary constants in hex rather than octal. These constants start with a 0x prefix, and are hard to miss. Also, octal digits encode three bits. Three is not a divisor of common word lengths used in computers today. That is, we have 8 bit bytes, 16, 32, 64 and 128 bit words. But in the old days, we used to have 12, 18, 36 and 60 bit words. Octal made more sense then, especially for 18 bit machines. The 8080 microprocessor has instructions which are easier to decode in octal than hex, because the "mov" family was 01ab, where a was the destination register and b was the source register. These machines are less common now.

But if i were God, and could redesign human civilization from scratch, it would be octal, not decimal. Mental arithmetic would be easier. Circles would be divided into 01000 (which is 512) degrees, and further subdivided with a decimal-like notation. The second would exactly be 1/0100000th of the day (1/32768th), which would make it about 2.6 times longer, and the phrase "just a second" would make a little bit more sense. By the time computers and C were designed, there'd be no reason to support decimal.

This would not be a good reason to make C support octal, as it still wouldn't match word lengths. A good reason for C to support octal is that it is a systems language.

One solution, that C didn't take, is direct support for binary. There are some macros for supporting binary, and they're ugly, but they work. Real men use binary. And, There are 10 kinds of people in the world. Those that understand binary, and those that don't.


/* Binary constant generator macro
* By Tom Torfs - donated to the public domain
*/
#define HEX__(n) 0x##n##LU

#define B8__(x) ((x&0x0000000FLU) ? 1 : 0) \
+ ((x&0x000000F0LU) ? 2 : 0) \
+ ((x&0x00000F00LU) ? 4 : 0) \
+ ((x&0x0000F000LU) ? 8 : 0) \
+ ((x&0x000F0000LU) ? 16 : 0) \
+ ((x&0x00F00000LU) ? 32 : 0) \
+ ((x&0x0F000000LU) ? 64 : 0) \
+ ((x&0xF0000000LU) ? 128 : 0)

/* for up to 8-bit binary constants */
#define B8(d) ((unsigned char)B8__(HEX__(d)))

/* for up to 16-bit binary constants, MSB first */
#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) + B8(dlsb))

/* for up to 32-bit binary constants, MSB first */
#define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) \
+ ((unsigned long)B8(db2)<<16) \
+ ((unsigned long)B8(db3)<<8) \
+ B8(dlsb))

#include <stdio.h>

main()
{
int i;
unsigned int a[] = {
B8(01010101),
B16(10101010,01010101),
B32(10000000,11111111,10101010,01010101)
};
printf("%d\n", atoi("016"));
for (i = 0; i < 3; i++) {
printf("entry = %u\n", a[i]);
}

}

No comments: