# convert 32 bit number to array of digits

This is a discussion on convert 32 bit number to array of digits within the C Programming forums, part of the General Programming Boards category; Hi, I am attempting to convert a 32-bit number into an array of its digits, the indices of which can ...

1. ## 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. 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

3. 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. 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. Originally Posted by droseman
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

6. Originally Posted by matsp
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. Originally Posted by ch4
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

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

9. 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. 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

11. 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. Originally Posted by droseman
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