Path: csiph.com!news.mixmin.net!eternal-september.org!reader01.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Re: Beginner....Decimal/Octal converter help Date: Fri, 09 Sep 2022 09:16:58 -0700 Organization: A noiseless patient Spider Lines: 196 Message-ID: <86leqsr091.fsf@linuxsc.com> References: <8ffd982c-2e82-4caa-9256-ec17be2efe56n@googlegroups.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: reader01.eternal-september.org; posting-host="79e2a8cba320b635c1b4a8520dc5642b"; logging-data="1151435"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/M5HGkVaqYCNGZTAoFeMROVmtaTpvW9II=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:4yZ8NeBIZPBh8btiXTWokJpwYw4= sha1:D9BN/x+uo18qPnO/wWlYijY77P8= Xref: csiph.com comp.lang.c:167560 ManyBeers writes: > Hello I am teaching myself C and I need some help with a problem. > After a search for dec/oct convert I found this code: > > [..code..] I would like to offer a different kind of answer to your questions. First a suggestion: for this kind of problem I think you will find it helpful to use unsigned types exclusively. In the code below we will need (besides a plain 'unsigned' here and there) two types, one for 8-bit values and one for 32-bit values: typedef unsigned char UC; typedef unsigned int UI; Next we need to talk about what you're doing, which is base conversion. That is to say, you have a number in one base, and you want to convert it to a different base. Properly speaking the word digit usually means a decimal digit, or a value between 0 and 9 inclusive. However, we are working in various bases, so I will use "digit" to mean a value in the appropriate range for the base in question. Now, what is a number in base B? It is a sequence of "digits" in base B, that is, values between 0 and B-1. A natural way to represent a number in base B is as an array of elements for the "digits", where each element can hold a value in the relevant range. For bases up to 256, these elements can be of type UC - an unsigned character can hold all values between 0 and 255, so it can hold all values between 0 and 9 (for decimal), or all values between 0 and 7 (for octal), or all values between 0 and 15 (for hexadecimal), etc. Here are some sample holders of numbers in different bases: UC decimal_197[] = { 1, 9, 7 }; UC octal_1777[] = { 1, 7, 7, 7 }; UC hex_FF[] = { 15, 15 }; (Slight digression: Notice that there is nothing marking the "end" of a number. To use these variables we need to know how many "digits" are held, which is the same as the number of elements in each array. There is a well-known idiom in C that computes what this is, given the variable name. That idiom is typically written as a macro, as for example #define NUMBER_OF( a ) (sizeof (a) / sizeof *(a)) We will see uses of this macro in the code below.) Okay, so we have some array variables holding numbers in different bases. To make use of these numbers we need to convert them into C's native numeric type, which here means the type UI. What is a value of type UI? Because a UI has 32 bits, a variable of type UI is a single "digit" in base 2**32, or 4294967296. Here is a function to convert a base-10 number to a UI, which is to say to a base-4294967296 number: UI base_10_to_base_4294967296( UC digits[], unsigned n ){ unsigned i; UI r = 0; for( i = 0; i < n; i++ ){ r = r * 10 + digits[i]; } return r; } Code to convert a base-8 number to a UI is almost identical: UI base_8_to_base_4294967296( UC digits[], unsigned n ){ unsigned i; UI r = 0; for( i = 0; i < n; i++ ){ r = r * 8 + digits[i]; } return r; } And it's easy to see how to generalize these functions into a single function that will handle any base between 2 and 256: UI base_b_to_base_4294967296( unsigned b, UC digits[], unsigned n ){ unsigned i; UI r = 0; for( i = 0; i < n; i++ ){ r = r * b + digits[i]; } return r; } Having written these, we are now ready to write a simple test driver: #include #define NUMBER_OF( a ) (sizeof (a) / sizeof *(a)) int main(){ UC decimal_197[] = { 1, 9, 7 }; UC octal_1777[] = { 1, 7, 7, 7 }; UC hex_FF[] = { 15, 15 }; unsigned decimal_197_digits = NUMBER_OF( decimal_197 ); unsigned octal_1777_digits = NUMBER_OF( octal_1777 ); unsigned hex_FF_digits = NUMBER_OF( hex_FF ); UI u197d = base_10_to_base_4294967296( decimal_197, decimal_197_digits ); UI u1777o = base_8_to_base_4294967296( octal_1777, octal_1777_digits ); UI uFFh = base_b_to_base_4294967296( 16, hex_FF, hex_FF_digits ); printf( " u197d is %7u decimal\n", u197d ); printf( "u1777o is %7u decimal\n", u1777o ); printf( " uFFh is %7u decimal\n", uFFh ); printf( "\n" ); printf( " u197d is %7o octal\n", u197d ); printf( "u1777o is %7o octal\n", u1777o ); printf( " uFFh is %7o octal\n", uFFh ); printf( "\n" ); printf( " u197d is %7x hexidecimal\n", u197d ); printf( "u1777o is %7x hexidecimal\n", u1777o ); printf( " uFFh is %7x hexidecimal\n", uFFh ); printf( "\n" ); return 0; } Notice that the UI variables are not inherently either decimal, octal, or hexadecimal. The printed values come out as decimal, octal, or hexadecimal, by virtue of what conversion specifier (u, o, or x) is used in the printf() format string. You see that? Now for the next step. The code above allows conversion from an arbitrary base (up to 256) to the base of C's native unsigned type. What we're looking for is a function that goes the other direction: from the base of C's native unsigned type to an array of "digits" in a smaller base. This function needs different interface. In particular, it should take a parameter that is a pointer to an array of UC, and should return a count of the number of digits in the result. Here is our first try: unsigned base_4294967296_to_base_b_X( UI value, unsigned b, UC out[] ){ UI v = value; unsigned k = 0; do { out[k] = v % b; k++; } while( v /= b, v != 0 ); return k; } This function sort of works, but it has a problem: the generated "digits" are backwards! An annoying artifact of conversions like this is they really want to start at "the wrong end". There are various ways to correct this problem, but probably the easiest to explain is to put digits into a temporary array, and then copy them in reverse order to the out[] array. And so, unsigned base_4294967296_to_base_b( UI value, unsigned b, UC out[] ){ UI v = value; unsigned k = 0; UC digits[100]; unsigned i; do { digits[k] = v % b; k++; } while( v /= b, v != 0 ); for( i = 0; i < k; i++ ){ out[ i ] = digits[ k-1-i ]; } return k; } Please take a look over the above code and explanations, and see if things make more sense now. Are there any questions you have? One more note, on a more advanced topic. In the code above we have limited the base-4294967296 numbers to only one UI, which is to say limited those numbers to only one "digit". If we want to represent very large numbers, we can have an _array_ of UI, with each element of the array holding a single base-4294967296 digit. Doing that is possible of course, but it's a lot harder, so while you're still learning C you might want to stick with the simpler cases explained above.