Thread: convert 32 bit number to array of digits

  1. #1
    Embedded in C...
    Join Date
    Sep 2008
    Location
    Basingstoke, Hampshire
    Posts
    83

    convert 32 bit number to array of digits

    Hi,

    I am attempting to convert a 32-bit number into an array of its digits, the indices of which can then be used to fill a structure. I obtained this code, but I am having a little difficulty in understanding some parts of it:

    Code:
    int * numArrayFill(u32 number) 
    {
        unsigned int length = (int)(log10((float)number)) + 1;              // calculates length of number
        int *numArray = (int *) (length * sizeof(int)), *curr = numArray;
        do {
            *curr++ = number % 10;
            number /= 10;
        } while (number != 0);
        
        return numArray;
    }
    The work of the function seems to be done by this line:
    Code:
    int *numArray = (int *) (length * sizeof(int)), *curr = numArray;
    but its this I don't understand.

    Also, as I assume that numArray is an array of ints, then do I have to then declare this as an array in my calling function?

    Thanks

    Dave

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    First of all, I would not use log10() to calculate the number of digits. Even if the processor has log10 capability in hardware, it is likely quite slow, and dividing the number up to 11 times by 10 will probably take no longer. Of course, we don't actually NEED to know the number of digits.

    It looks to me like you are missing a malloc in the "numArray = ..." line (or something else like that).

    I will be back with something I think is better.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    Well, the code is utterly wrong . And will probably crash when ran.
    The line you copied is 'supposed' to allocate enough memory for the digits. Of course, it does nothing of the sort and makes no sense whatsoever. If you'd add a malloc it'd make some sense, but it would still not be impressive code :P.

    First of all, why use integers to store the digits in stead of chars? Also, there's no way of knowing the length here.

    Don't use this code. In fact, the source of this code, never use it again :P. Just a small advice . May be better to use sprintf, or write your own similar function.

  4. #4
    Embedded in C...
    Join Date
    Sep 2008
    Location
    Basingstoke, Hampshire
    Posts
    83
    I don't really want to use malloc, as i am on a limited resources embedded platform, and my compiler doesn't undersand malloc anyway.

    As a bit of background, this integer (and its generated by an incremental counter, so it must be int) is used to fill up to five members of a struct which is then sent to a serial line one byte at a time.

    First of all, I would not use log10() to calculate the number of digits. Even if the processor has log10 capability in hardware, it is likely quite slow, and dividing the number up to 11 times by 10 will probably take no longer.
    That seems like fair reasoning, could you make a good alternative suggestion?

    Of course, we don't actually NEED to know the number of digits.
    How do I know how many members to fill if I don't know how many digits I have in the integer?

    The original code did contain malloc, but I removed it for the reasons above.

    --dave

  5. #5
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by droseman View Post
    I don't really want to use malloc, as i am on a limited resources embedded platform, and my compiler doesn't undersand malloc anyway.

    As a bit of background, this integer (and its generated by an incremental counter, so it must be int) is used to fill up to five members of a struct which is then sent to a serial line one byte at a time.



    That seems like fair reasoning, could you make a good alternative suggestion?



    How do I know how many members to fill if I don't know how many digits I have in the integer?

    The original code did contain malloc, but I removed it for the reasons above.

    --dave
    A 32-bit unsigned number can not be more than 4294967295, which is 10 digits. So having a fixed string size of 10 will work.

    It then depends on what you need to do with the digits. The above code shows them in "backwards order". Is that what you need?

    To reduce memory usage, I would use a passed-in array of char [11] - that is enough to store any 32-bit unsigned number - for signed you may need one more digit.

    Would passing an array in work for you?

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  6. #6
    Registered User ch4's Avatar
    Join Date
    Jan 2007
    Posts
    154
    Quote Originally Posted by matsp View Post
    First of all, I would not use log10() to calculate the number of digits.
    An extra question here

    I there another way as simple as log10 is, to calculate the number of digits ?

    I can make function to count this number, but i don't think that it is faster than log10.

  7. #7
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by ch4 View Post
    An extra question here

    I there another way as simple as log10 is, to calculate the number of digits ?

    I can make function to count this number, but i don't think that it is faster than log10.
    Depends on the system you are using, whether it can do log10() quickly or not. But it's not JUST the log10 that matters - you get a whole bunch of floating point math operations, that then need to synchronize back to the integer unit.

    I did a quick little benchmark of just figuring out the number of digits [yes, it's a quick hack - but I do validate the result]:
    Code:
    #include <stdio.h>
    #include <time.h>
    #include <math.h>
    #include <stdlib.h>
    
    int numDigitsLog10(int n)
    {
        return 1 + log10((float)n);
    }
    
    int numDigitsDivide(int n)
    {
        int d = 0;
        do
        {
    	d++;
        } while(n /= 10);
        return d;
    }
    
    
    int numDigitsRecurse(int n)
    {
        if (n < 10) 
        {
    	return 1;
        }
        else 
        {
    	return 1 + numDigitsRecurse(n / 10);
        }
    }
    
    #define test(x) { #x, x }
    
    struct testcase
    {
        char *name;
        int (*func)(int);
    } testcases[] =
    {
        test(numDigitsLog10),
        test(numDigitsDivide),
        test(numDigitsRecurse)
    };
    
    const int numIters = 10000000;
    
    int main()
    {
        struct
        {
    	int n;
    	int d;
        } array[] = 
    { 
        {       100, 3 }, 
        {      1234, 4 },
        {    631271, 6 },
        { 567801234, 9 }
    };
        int i, j, k;
        for(i = 0; i < sizeof(testcases)/sizeof(testcases[0]); i++)
        {
    	clock_t t = clock();
    	for(j = 0; j < numIters; j++)
    	{
    	    for(k = 0; k < sizeof(array)/sizeof(array[0]); k++)
    	    {
    		int d = testcases[i].func(array[i].n);
    		if (d != array[i].d)
    		{
    		    printf("testcase %s failed: for %d expected %d, got %d\n",
    			   testcases[i].name, array[i].n, array[i].d, d);
    		    exit(1);
    		}
    	    }
    	}
    	t = clock() - t;
    	printf("testcase %s: %6.2f\n", 
    	       testcases[i].name, (double) t / CLOCKS_PER_SEC); 
        }
        return 0;
    }
    Results, using gcc-mingw -O2 -ffast-math:
    Code:
    testcase numDigitsLog10:   6.53
    testcase numDigitsDivide:   0.81
    testcase numDigitsRecurse:   1.59
    The exact results will of course depend on the processor model. But log10 is not a FAST operation in the floating point unit in any processor.

    Actually, whilst writing this reply, I came up with another possible solution, which is faster yet:
    Code:
    int numDigitsCompare(int n)
    {
        if (n >= 1000000000)
    	return 10;
        if (n >= 100000000)
    	return 9;
        if (n >= 10000000)
    	return 8;
        if (n >= 1000000)
    	return 7;
        if (n >= 100000)
    	return 6;
        if (n >= 10000)
    	return 5;
        if (n >= 1000)
    	return 4;
        if (n >= 100)
    	return 3;
        if (n >= 10)
    	return 2;
        return 1;
    }
    Which results in 0.22 seconds runtime (for 10 million times). I have an idea of how to make that one a bit faster too...

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  8. #8
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    How about this (as a complete working test-framework too!)

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    typedef unsigned int u32;
    
    
    char *numToStr(u32 number, char *buffer, int size) 
    {
        char *curr = &buffer[size]; // Yes, one beyond it's actual size. We use --curr.
        *--curr = 0; // Mark end of string. 
        do {
            *--curr = (number % 10) + '0';
            number /= 10;
        } while (number != 0);
    
        // Check if the whole number fits!
        if (curr == buffer && number != 0)
    	return NULL;
        
        return curr;
    }
    
    int main()
    {
        char buf[11];
        char *str;
        
        str = numToStr(4711923, buf, sizeof(buf));
        printf("%s\n", str);
        return 0;
    }
    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  9. #9
    Embedded in C...
    Join Date
    Sep 2008
    Location
    Basingstoke, Hampshire
    Posts
    83
    thanks for that mats - while reading through the code, the operation of the do-while loop just clicked into place

    But it's working in reverse to what I was trying to achieve. To illustrate, here is my struct initialisation:

    Code:
        MsgSend_eti.space01 = 0x20;
        MsgSend_eti.time_ten_thou = /**/;
        MsgSend_eti.space02 = 0x20;
        MsgSend_eti.time_one_thou = /**/;
        MsgSend_eti.space03 = 0x20;
        MsgSend_eti.time_hundrd = /**/;
        MsgSend_eti.space04 = 0x20;
        MsgSend_eti.time_ten = /**/;
        MsgSend_eti.space05 = 0x20;
        MsgSend_eti.time_unit = /**/;
        MsgSend_eti.CR = 0x0D;
        MsgSend_eti.LF = 0x0A;
    The members with the comment blocks are the numbers of hours, ten-hours etc. Thhe input for this is an incremental counter of int, so for 40012 hours I would need:

    Code:
     MsgSend_eti.space01 = 0x20;
        MsgSend_eti.time_ten_thou = hoursArray[4];  // 4
        MsgSend_eti.space02 = 0x20;
        MsgSend_eti.time_one_thou = hoursArray[3]; // 0
        MsgSend_eti.space03 = 0x20;
        MsgSend_eti.time_hundrd = hoursArray[2];    // 0
        MsgSend_eti.space04 = 0x20;
        MsgSend_eti.time_ten = hoursArray[1];        // 1
        MsgSend_eti.space05 = 0x20;
        MsgSend_eti.time_unit = hoursArray[0];       // 2
        MsgSend_eti.CR = 0x0D;
        MsgSend_eti.LF = 0x0A;
    I can see how your code would enable me to get the array out of an integer (I assume the function would need to be int *), but not sure on what I need to pass and return to make it work.

    --dave

  10. #10
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    So you WANT it backwards. Ok.
    Code:
    char *numToStrRev(u32 number, char *buffer, int size) 
    {
        char *curr = buffer; 
        do {
            *curr++ = (number % 10) + '0';
            number /= 10;
        } while (number != 0 && curr != buffer);
        // Check if the whole number fits!
        *curr++ = 0; // Mark end of string. 
        if (curr == buffer && number != 0)
    	return NULL;
        return buffer;
    }
    also, if you ALWAYS want 5 digits, we could do it differently:
    Code:
    char *numToStrRev5(u32 number, char *buffer, int size) 
    {
        char *curr = buffer; 
        int i;
        assert(size >= 6);
        for(i = 0; i < 5; i++)
        {
            *curr++ = (number % 10) + '0';
            number /= 10;
        }
        // Check if the whole number fits!
        *curr++ = 0; // Mark end of string. 
        if (curr == buffer && number != 0)
    	return NULL;
        return buffer;
    }
    Of course, the 5 could be converted to use size-1 or a fourth parameter.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  11. #11
    Embedded in C...
    Join Date
    Sep 2008
    Location
    Basingstoke, Hampshire
    Posts
    83
    I just realised that I led you astray a bit when I mentioned about needing the function in reverse.

    I was thinking about string in array out, but the first example does that. Whn I tested it, it crashed when I tried to access a single member with printf.

    When you remember to use %c instead of %s it works fine

  12. #12
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by droseman View Post
    second listing did the trick (almost)

    when I put in 40012, I got out 21004, but I was able to access the elements of the array.

    Is this the endian-ness of my computer or something that needs to change in the code ?

    --dave
    No, it's not endian-ness. It's just about how you store numbers in an array. Storing the units first, or store the units last.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 2
    Last Post: 02-08-2009, 09:26 PM
  2. adding a number to a number
    By bigmac(rexdale) in forum C Programming
    Replies: 11
    Last Post: 10-24-2007, 12:56 PM
  3. Learning Memory, Ins and Outs?
    By Zoiked in forum C Programming
    Replies: 1
    Last Post: 08-27-2007, 04:43 PM
  4. Issue w/ Guess My Number Program
    By mkylman in forum C++ Programming
    Replies: 5
    Last Post: 08-23-2007, 01:31 AM
  5. Hi, could someone help me with arrays?
    By goodn in forum C Programming
    Replies: 20
    Last Post: 10-18-2001, 09:48 AM