Page 1 of 1

printing integers

Posted: Fri May 16, 2008 10:58 am
by Woellchen
i have a little problem..
i have a function which prints strings on the screen(puts()) but i want it to print an integer on the screen
but the problem is, that i have to convert the integer first
i dont know how to do so
ive already tried to use a cast like: (char) integer_variable but it doesnt seem to work
i hope u have an answer to that
thanks

Posted: Fri May 16, 2008 12:15 pm
by AJ
Hi,

Have a look at this link which has a decent section on number conversion (despite being an AVR rather than x86 site).

The basic rule for decimal is that you divide by decreasing factors of 10, appending ASCII numbers to the string you are creating. For hex you do the same, but you can use bit shifting because you deal with a whole nibble at a time.

You cannot use a cast in the way you mention. Supposing yout integer_variable = 0x12345678, cast it and pass it to a putchar function, the function will try to print the ASCII value of 0x78, which is the character 'N'.

Cheers,
Adam

Re: printing integers

Posted: Fri May 16, 2008 12:15 pm
by JamesM
Woellchen wrote:i have a little problem..
i have a function which prints strings on the screen(puts()) but i want it to print an integer on the screen
but the problem is, that i have to convert the integer first
i dont know how to do so
ive already tried to use a cast like: (char) integer_variable but it doesnt seem to work
i hope u have an answer to that
thanks
You have to make an algorithm up yourself to do it (there are several standard ones out there). You can't do it with a cast - there are a number of steps involved.

Posted: Fri May 16, 2008 1:30 pm
by Woellchen
wow thanks this link helps me very much
ill write my own algorithm now
thanks ;)

Posted: Sat May 17, 2008 5:39 pm
by Woellchen
ok i have a problem now
i bet its quite easy but i cant fix it

Code: Select all

char longtostr( long zahl )
{
	char text[20];
	int i = 0;
	int c = 0;
	if( zahl == 0 ) return "0";
	while( zahl >= 100 ) zahl -= 100, ++c;
	text[i++] = c + 48, c = 0;
	while( zahl >= 10 ) zahl -= 10, ++c;
	text[i++] = c + 48, text[i] = zahl + 48;
	return text;
}
at the moment the code can only handle numbers to 999 but thats not the problem
the code is working actually but the problem is the returning
when i print the text variable in the function itself the number is displayed but when i print the returning theres nothing but some special chars or so
whats wrong with my code, hope you can help me
thanks

Posted: Sat May 17, 2008 5:45 pm
by Alboin
You're returning a char array, but your declaration is only for a char.

Try listening to your warnings. ;)

Posted: Sat May 17, 2008 6:09 pm
by Woellchen
oh yeah, ure right
i changed the text variable to a pointer and now it works ;)
thank you very much for your help

Posted: Mon May 19, 2008 12:32 am
by Ready4Dis
That function works, but programmatically, there are simpler ways, for example:

Code: Select all

char* longtostr( long zahl ) 
{ 
   static char text[20];   //Make me static, otherwise it's on the stack and will screw up soon, if it's static, it's allocated always, but is not safe for multi-tasking/threading.
   int loc=19;
   text[19] = 0; //NULL terminate the string
   while (zahl)  //While we have something left, lets add a character to the string
   {
       --loc;
       text[loc] = (zahl%10)+'0';
       zahl/=10;
   }
   if (loc==19) //Nothing, lets at least put a 0 in there!
   {
      --loc;
       text[loc] = '0';
   }
   return &text[loc];  //Start from where loc left off
}
Another solution is to take in an argument for an array + length, then try filling that in, this will make it safe for a multi-threaded environment ;). So, something like this:

Code: Select all

void longtostr( long zahl , char *buffer, long max) 
{ 
   int tmp, size;
   if (!zahl)  //check for 0 first thing, assumes max>=2
   {
     buffer[0] = '0';
     buffer[1] = 0;
     return;
   }
   tmp= zahl; //Temporary
   size = 0;
   while (tmp)  //Calculate the size of the string we require
   {
       tmp/=10;
       ++size;
   }
   if (size >= max-1) //Error, overflow, we assume max >= 5 :)
   {
       buffer[0] = 'O';
       buffer[1] = 'v';
       buffer[2] = 'f';
       buffer[3] = 'l';
       buffer[4] = 'w';
       buffer[5] = 0;
   }
   buffer[size] = 0; //Set the size
   while (zahl)  //While we have something left, lets add a character to the string
   {
       --size;
       buffer[size] = (zahl%10)+'0';
       zahl/=10;
   }
}
This second one isn't perfect, but much better, you can add checks for max == 0, or max==1 and return an error, or if an overflow condition happens, and max is <5, then you can return an error as well.

How this works:
zahl = 173
zahl % 10 = 3 (remainder of 173 / 10)
zahl / 10 = 17 (integer math)

zahl % 10 = 7
zahl / 10 = 1

zahl % 10 = 1
zahl / 10 = 0

While loops see's zahl = 0, and breaks, so when we placed 3,7,1 into our buffer backwards, forwards it is 173 :). This works for any sized number, and is much more intuitive than checking each value, and is also multi-threading safe, since it can work on multiple arrays at a time.

--- Edit ---
Oops, fixed code tags... and code bug(s)

Posted: Mon May 19, 2008 8:16 am
by Woellchen
wow thanks ready, its cool that u explain how it works but i already got it out of here: http://www.avrbeginners.net/
my current code is this btw:

Code: Select all

char longtostr( long zahl )
{
	char *text = 0;
	int i = 0;
	int j = 0;
	int c = 0;
	int s = 0;

	long *zahlp = &zahl + 4;
	while( *zahlp++ ) s++;

	if( zahl == 0 )
	{
		text[0] = '0';
		text[1] = '\0';
		return text;
	}

	for( j = 1 ; j < s + 1 ; j++ )
	{
		while( zahl >= pow( 10, s - j ) ) zahl -= pow( 10, s - j ), c++;
		if( c > 0 ) text[i++] = c + 48, c = 0;
	}

	text[i++] = zahl + 48;
	text[i] = '\0';

	return text;
}
and it works ;)
i understand the multitask/thread issue, thank you for that, ill change that
but is my code still acceptable then?
while (loc) //Calculate the size of the string we require
u do a while loop with the variable loc, without initialising it
could u do a correction on that?

thank you

Posted: Mon May 19, 2008 10:08 am
by Ready4Dis
Sorry, fixed it, good catch. I typed it all in the window, cut and paste the first to write the second, and forgot to change the variable name there :). Anyways, no problem for the explanation, just wanted to mention that your method, while it may work, it is much less efficient due to the while loop and all the pow calls, where my method runs very fast in all cases.

Couple of issues with your code:
What is with the zahlp. I am not sure what you're trying to do, but it's a bad idea, taking the memory address of an argument to the function will give you a pointer to the stack. the problem is, after your parameter in the stack, it will have the address of the function call, so it will increment until it finds a NULL value in your stack (there may never be one until it over-runs your stack and causes a page fault).
Also, you declare char *text = 0; Then you use it directly, which means you're physicall writing to 0x00000000 in memory, which will over-write your real mode interrupt vector table (fi you don't use v86 or real-mode, this is ok, but just realize that it's not typically considered good programming to write to 0x00000000, since it's typically used to check for NULL). I would suggest using a variant of my method, it is probably one of the most used, and most efficient methods (in C anyways, in ASM you can use a div and get the % for free).


Your coding concerns me, using a NULL pointer, referencing the stack like you are, etc. You may want to get a bit more familiar with how function calls are done (what actually happens on the stack), how memory is treated (pointers, vaiables, local variables, etc), the difference between normal and static variables in functions, etc. It will help you in writing code that doesn't have so many potential problems.

Posted: Mon May 19, 2008 12:23 pm
by Woellchen
yea sorry, the *text = 0 declaration is not really good, but i knew that i have written to 0x00000000
later i had changed it to *text = malloc() or so as soon as i had implented memory management
but i think i will use a variant of yours anyway

and thank you for ur corrections in my code, helps me being better in os-dev ;)

EDIT:
text[size] = (zahl%10)+'0';
im sure u mean
buffer[size] = (zahl%10)+'0';
dont you?

Posted: Mon May 19, 2008 9:40 pm
by Ready4Dis
Yes, again remanents of my copy/paste from above and chaning variable's to be more descriptive. That said, using malloc inside of a function as this will be very wasteful (most memory managers are relatively slow, so allocating and deallocating such small memory buffers will waste a lot of time), it's simpler to just say, it's a 4-byte integer, what's the largest value i can store, then make a buffer that large for it. Depending on if it has to be multi-thread safe, you can use a static buffer, or pass a buffer to it (or as you're doing, allocate the buffer, but then who is responsible to deallocate the buffer? You can't deallocate it until it is displayed, so you would have to remember to store the buffer to delete later, not generally a good thing).

Posted: Sat May 24, 2008 9:43 pm
by FrostFire
Here is my modified version of one of the methods above. This one will correctly handle negative numbers.

If the number is positive, it will convert it to a negative number, as opposed to converting it to a positive number if it is negative. This allows it to correctly handle the minimum integer value -2,147,483,648.

I also changed the char array to only have 12 elements (minus sign, 10 digits, '\0')

Code: Select all

char* itoa(int i)
{
	   static char text[12]; 
	   int loc = 11; 
	   text[11] = 0; 
	   char neg = 1;
	   if (i >= 0)
	   {
		   neg = 0;
		   i = -i;
	   }
	   while (i) 
	   { 
	       text[--loc] = '0' - (i%10); 
	       i/=10; 
	   }
	   if (loc==11) 
	       text[--loc] = '0'; 
	   if (neg)
		   text[--loc] = '-';	   
	   return &text[loc]; 
}