9 Replies - 612 Views - Last Post: 30 June 2012 - 06:50 AM Rate Topic: -----

#1 jink  Icon User is offline

  • D.I.C Head

Reputation: 5
  • View blog
  • Posts: 63
  • Joined: 10-July 11

Imitating printf function , getting output wrong

Posted 30 June 2012 - 02:59 AM

this is a sum from kernighan and ritchie. we have to make a function minprintf which behaves like printf.we can use putchar in the function.
I have written the function.here is the code.
filename ./k\&r7-3.c
/*program does not work if we give a huge float or double*/
/*len,precision and field_width should be initialsed before using*/
/*do field_width and precision functions need to be separated out?*/
/*right allignment or left allignment need to check*/
/*in switch statement some statements are repeated,make them common??*/
/*enable * usage as in printf*/
/*enable exponential power display,hexadecimal and octal number display*/
/*link itoa,roundoff,dotoa*/

#define MAXFIELD 5
#define MAXLEN 30
#define DEFAULT_PRES 6
#include<stdio.h>
#include<ctype.h>
#include<string.h>//for strlen
#include<stdlib.h>//only for atoi,could have as well used my own
#include <stdarg.h>

extern void itoa(int,char*);
extern void print_arr(char*);
extern void roundoff(char*,int);
extern void dotoa(double,char*);

/* minprintf: minimal printf with variable argument list */
void minprintf(char *fmt, ...){
	va_list ap; /* points to each unnamed arg in turn */
	char *p, *sval;
	char field_store[MAXFIELD];
	char pres_store[MAXFIELD];//precision also number of digits max kept same as field no of digits 
	char arguments[MAXLEN];
	int ival,field_width,precision,i=0,len;//i is for arguments array,len is for length of number in arguments[]
	short int right_allign=1;//by default right alligned
	double dval;

	va_start(ap, fmt); /* make ap point to 1st unnamed arg */
	for (p = fmt; *p; p++) {
		if (*p != '%') 
			putchar(*p);
		else{
			/*right allign or left allign check and assign right_allign accordingly*/
			if((*++p)=='-')
				right_allign=0;
			else{
				right_allign=1;
				--p;//'-' not there instead digit there probably
			}
			/*field_width and precision input*/
			/*if it is not explicitly declared then there has*/
			/*to be some default value or the previous value will be taken*/
			pres_store[0]='\0';
			field_store[0]='0';field_store[1]='\0';//field width can be safely initialised to 0
			/*because its implementation is same for all*/
			if(isdigit(*++p)){
				i=0;//for moving along field store array
				while(!isalpha(*p) && (*p)!='.')
					field_store[i++]=*(p++);
				field_store[i]='\0';
				i=0;//now for filling pres_store[]
				if(*p=='.'){
						printf("\nstoring in pres_store:");//testing
						while(isdigit(*++p)){
							pres_store[i++]=(*p);
							printf("%c ",pres_store[i-1]);//testing
						}
						putchar('\n');//testing
				}
				pres_store[i]='\0';
				printf("\nafter writing in the pres_store array:");//testing
				print_arr(pres_store);//testing
				i=0;//keeping i in initial condition
			}
			
			switch (*p) {//pointer will come till here while checking isnum
			case 'd':
				ival = va_arg(ap, int);
				itoa(ival,arguments);
				
				/*precision manage*//*this is taken before fields this because fields thing only deals with printing*/
				/*precision has different meaning for integers*/
				precision=atoi(pres_store);
				len=strlen(arguments);
				if(precision=='\0')//that means it is not specified
					precision=0;	
				if(len<=precision)
					;//here we will take precision
				else
					precision=len;//then we are going to take length
				
				/*fields manage*//*just adds spaces when necessary*/
				field_width=atoi(field_store);
				if(right_allign==1){
					i=1;//initialising before using it
					while((i++)<=(field_width-precision))//if field width<len then loop wont start,since precision is number of digits
						putchar(' ');
					i=1;
					while((i++)<=precision-len)
						putchar('0');
					i=0;//leaving i as it was before,ready for another loop
				}
				else{/*in this case add spaces to the *array* *///left_allign
					i=len;
					while(i<=(field_width-precision)){
						arguments[i++]=' ';
					}
					arguments[i]='\0';
					i=1;
					while((i++)<=(precision-len))
						putchar('0');
					i=0;
				}
				print_arr(arguments);
				break;
			case 'f':
				dval = va_arg(ap, double);
				dotoa(dval,arguments);
				/*precision manage*//*if not given 6 decimal places*/
				if(pres_store[0]!='\0'){//array not empty
					precision=atoi(pres_store);
					printf("\nprinting precision:");//testing
					print_arr(pres_store);//testing	
				}
				else//default precision 6
					precision=DEFAULT_PRES;
				printf("\ndouble precision:%d\n",precision);//testing
				roundoff(arguments,precision);/*roundoff to precision number of places*/
				/*field manage*/
				len=strlen(arguments);//length may have changed after precision
				field_width=atoi(field_store);
				putchar('\n');//testing
				print_arr(arguments);//testing
				if(right_allign==1){
					i=1;
					while((i++)<=(field_width-len))
						putchar(' ');
					i=0;//leave i in original position
				}
				else{//left align
					int temp=field_width-len;
					i=len-1;//pointing at last element
					while((temp--)>0)
						arguments[i++]=' ';
					arguments[i]='\0';
					i=0;//assigning i to the initial value
				}
				print_arr(arguments);
				break;
			case 's':
				for (sval = va_arg(ap, char *),i=0; *sval; sval++)
					arguments[i++]=(*sval);
				/*precision manage*/
				precision=atoi(pres_store);
				if(precision=='\0')
					precision=MAXLEN;//biggest arguments size that can be
				len=strlen(arguments);
				if(len<=precision)
					precision=len;//precision is number of digits going to be printed
				else
					arguments[precision]='\0';//eg:if array is a,b,h,y and precision 2 then array[2]='\0'
				/*field manage*/
				field_width=atoi(field_store);
				if(right_allign==1){
					i=1;//initialising before using it
					while((i++)<=(field_width-precision))//if field width<len then loop wont start,since precision is number of digits
						putchar(' ');
					i=0;//leaving i as it was before,ready for another loop
				}
				else{/*in this case add spaces to the *array* *///left_allign
					i=len;
					while(i<=(field_width-precision)){
						arguments[i++]=' ';
					}
					arguments[i]='\0';
					i=0;
				}
				print_arr(arguments);
				break;
			case 'c':
				putchar(va_arg(ap,int));
				break;
			
			default:
				putchar(*p);
				break;
			}
		}
	}	
	va_end(ap); /* clean up when done */
}

void print_arr(char array[]){
	int i;
	for(i=0;array[i]!='\0';++i)
		printf("%c",array[i]);
	return;
}

the main function for testing the function is as follows.
/*for testing minprintf*/
#include<stdio.h>

extern void minprintf(char*,...);

int main(){
	int a=4;
	char s[]="dumo";
	minprintf("hump %7.3d:%4.3s %2.2f %c\n",a,s,4.195689,'x');
	return 0;
}

no warnings or errors while compiling.
but in case 'f',line 113 printing the pres_store array prints the arbitrary value 750 which should have been the precision of the double number.printing the same array before the switch statement is giving the correct value of 2.
I am not able to understand why this is happening.
because the precision value is set to 750 somehow i am not able to roundoff my number to 2 places of decimal.
Command:gcc -Wall ~/todel.c ./k\&r7-3.c ./my_template_func/itoa.c ./my_template_func/roundoff.c ./my_template_func/dotoa.c -o ./k\&r7-3

Output without the testing statements:
hump 004: dum 4.195688999999999779788595333229750 x

Output with testing statements:
hump
storing in pres_store:3

after writing in the pres_store array:3 004:
storing in pres_store:3

after writing in the pres_store array:3 dum
storing in pres_store:2

after writing in the pres_store array:2
printing precision:750
double precision:750

4.1956889999999997797885953332297504.195688999999999779788595333229750 x

Incorrect output is occuring first at line 120 of minprintf function and then at line 123.
i have not included itoa,roundoff,and dotoa(double to array) code here.those codes are mostly correct.if needed tell me.

Is This A Good Question/Topic? 0
  • +

Replies To: Imitating printf function , getting output wrong

#2 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2085
  • View blog
  • Posts: 3,170
  • Joined: 21-June 11

Re: Imitating printf function , getting output wrong

Posted 30 June 2012 - 03:34 AM

When I run your code in valgrind (after adding the missing function implementations, so that it actually compiles...), I get "Conditional jump or move depends on uninitialised value(s)" on line 154: len=strlen(arguments);. And sure enough you never initialize arguments.
Was This Post Helpful? 1
  • +
  • -

#3 jink  Icon User is offline

  • D.I.C Head

Reputation: 5
  • View blog
  • Posts: 63
  • Joined: 10-July 11

Re: Imitating printf function , getting output wrong

Posted 30 June 2012 - 03:47 AM

yeah i missed the statement
arguments[i]='\0';

after line 150.
but i have initialised arguments on line 076,115 and 149.
output remains the same.
Was This Post Helpful? 0
  • +
  • -

#4 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2085
  • View blog
  • Posts: 3,170
  • Joined: 21-June 11

Re: Imitating printf function , getting output wrong

Posted 30 June 2012 - 03:57 AM

I can't seem to reproduce your issue. When I fix the issue with arguments, I get no errors in valgrind and I get the correct output either way.

Maybe your implementation of itoa or dotoa is wrong? Have you tried running your program through valgrind yourself?
Was This Post Helpful? 0
  • +
  • -

#5 jink  Icon User is offline

  • D.I.C Head

Reputation: 5
  • View blog
  • Posts: 63
  • Joined: 10-July 11

Re: Imitating printf function , getting output wrong

Posted 30 June 2012 - 04:16 AM

I didnt know about valgrind until you mentioned it.so i dont have it and i have not tried it.

Quote

and I get the correct output either way.

printing the number to 2 decimal places is the correct output.the outputs that i have mentioned are wrong.those are the ones that i am getting.hump 004: dum 4.20 x is the correct output according to printf.
did you get this output?
Was This Post Helpful? 0
  • +
  • -

#6 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2085
  • View blog
  • Posts: 3,170
  • Joined: 21-June 11

Re: Imitating printf function , getting output wrong

Posted 30 June 2012 - 04:35 AM

The output I get is:
hump 
storing in pres_store:3 

after writing in the pres_store array:3    004:
storing in pres_store:3 

after writing in the pres_store array:3 dum 
storing in pres_store:2 

after writing in the pres_store array:2
printing precision:2
double precision:2

4.194.19 x



This isn't the same as your printf output because a) it's full of debug output and b) the number is truncated, not rounded. However both of those are expected¹ and what I don't get is the precision being overridden to 750 (or any other value).

Obviously code that invokes undefined behavior can work fine on one system while breaking down on another, but if you were writing out of bounds anywhere or using uninitialized memory, Valgrind would tell me about that even if the code happened to produce the correct output on my system.

Of course there are many forms of UB that Valgrind would not detect, but currently I think the most likely assumption is simply that the mistake is in one of the functions that you didn't post and thus I recreated the functions on my end without reproducing the mistake.

¹ Of course b) is only expected because my roundoff function doesn't actually do any rounding, but just cuts the number off.

This post has been edited by sepp2k: 30 June 2012 - 04:37 AM

Was This Post Helpful? 1
  • +
  • -

#7 jink  Icon User is offline

  • D.I.C Head

Reputation: 5
  • View blog
  • Posts: 63
  • Joined: 10-July 11

Re: Imitating printf function , getting output wrong

Posted 30 June 2012 - 04:59 AM

i am posting the auxilliary functions as well.
roundoff.c
/*program works*/
/*roundoff a decimal number*/
/*how this will be done is that first check whether digit >,< or = 5*/
/*then set flags for next iteration number.then take care of current digit*/
void roundoff(char number[],int places){//places is decimal place upto which you want to round off
	int i=0,point_place=0;
	short int increment=0,five=0;//cant be written inside loop since every iteration they are assigned
	while(number[i]!='.' && number[i]!='\0')
		++i;
	if(number[i]=='\0')
		return;
	else{
		point_place=i;
		while(number[i]!='\0')
			++i;
		--i;//we go to the secondlast
		while(i>=point_place+places){//this while loop is for rounding off the previous digit
			int num_check;//checks the digit
			/*check whether to increment left side digit or not*/
			if(increment==1){
				num_check=(number[i]-'0')+1;
			}
			else
				num_check=(number[i]-'0');
			/*check whether current digit is five*/
			if(five==1){
				if((number[i]-'0')%2==0)
					num_check=(number[i]-'0');
				else
					num_check=(number[i]-'0')+1;
			}
			/*set flags for next number on the basis of the current one*/
			if(num_check>5){
				increment=1;
				five=0;
			}
			else if(num_check<5){
				increment=0;
				five=0;
			}
			else{
				five=1;
				increment=0;
			}
			/*increment or keep same the current digit*/
			if(num_check==10)
				number[i]='0';
			else
				number[i]=num_check+'0';				
			--i;
		}
		number[i+2]='\0';//e.g in 3.14956 to 2 dec places rondoff pointer has gone to 1.
	}
	return;
}

itoa.c
/*itoa.c*/
void itoa(int number,char v[]){
	int rmdr,i=0;//rmdr=remainder
	extern void reverse(char *);
	while(number!=0){
		rmdr=number%10;
		number-=rmdr;
		number/=10;
		v[i++]='0'+rmdr;
	}
	v[i]='\0';
	reverse(v);
	return;
}

/*reverse*/
void reverse(char torvs[]){//torvs=to reverse
	int forward=0,backward=0;
	int temp;
	while(torvs[backward]!='\0')
		++backward;//
		--backward;//now it points to last element of array
	while(backward>forward){
		temp=torvs[backward];
		torvs[backward]=torvs[forward];
		torvs[forward]=temp;
		--backward;
		++forward;
	}
	return;
}



dotoa.c
/*dotoa*/
/*in case double number is very big int will overflow and this version of code*/
/*wont work,for that we will have to deal with float and double number representation*/

void dotoa(double number,char s[]){
	int temp,i;
	double tempo;
	void itoa(int , char *);//declaration
	temp=(int)number;
	itoa(temp,s);//this should fill the last position as '\0'
	tempo=number;
	for(i=0;s[i]!='\0';++i)
		;
	s[i++]='.';
	do{
		tempo=tempo-(double)temp;//strips the integral part and leaves the decimal part eg.3.14->0.14
		tempo*=10.0;//0.14->1.4
		temp=(int)tempo;//1.4 stores 1
		s[i++]='0'+temp;//1 is added in array
	}while(temp!=0);//when 4 is also complete 0 comes
	s[i]='\0';
	return;
}

i was not posting them because they would have increased complications.they were previously tested by me.
i think only roundoff has error.it does roundoff but does not proceed beyond the required number of decimal places for roundoff.it will print output 4.10 instead of 4.20.
if you get any error in my functions please post.
i will be searching the errors after 1 or 2 hours.i am keeping the thread open.
Was This Post Helpful? 0
  • +
  • -

#8 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2085
  • View blog
  • Posts: 3,170
  • Joined: 21-June 11

Re: Imitating printf function , getting output wrong

Posted 30 June 2012 - 05:19 AM

Your mistake must be in the dotoa function. I replaced my functions with your functions one by one, running the program after each replacement, and the mistake didn't materialize until I replaced my dotoa function with yours.

Note that you defined MAXLEN to be 30, but the string created by dotoa is 36 characters long. So it's definitely going to write out of bounds. And as it happens the character sequence "750" appears in the output after the 30-character mark. So your dotoa function is writing 750 into the variable that stores the precision.

You should really design your functions in such a way that they can check against buffer overflows. The best thing to do here would be to let dotoa accept the precision as an argument instead of having a separate function to do the rounding. Then you just need to make sure that the precision is small enough to fit into the buffer.
Was This Post Helpful? 2
  • +
  • -

#9 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2085
  • View blog
  • Posts: 3,170
  • Joined: 21-June 11

Re: Imitating printf function , getting output wrong

Posted 30 June 2012 - 05:25 AM

Oh and as a tip for the future:

View Postjink, on 30 June 2012 - 01:59 PM, said:

i was not posting them because they would have increased complications.they were previously tested by me.


If you think a certain part of the code is not causing the error, remove that part of the code and see whether the error still occurs. Just because a piece of code works fine on its own doesn't mean it won't cause problems when interacting with other parts of the code.

Also you should always make sure that the code you post to a forum actually compiles (unless the problem you're having is a compile error of course) and produces the same error as your actual code. If people have to actually write their own versions of functions you left out before they can run your code, the chance that people will bother to debug your code for you is much smaller than if they can just copy and paste your code and it runs.

And if the code you posted doesn't actually contain the mistake, it's of course impossible for people to help you.
Was This Post Helpful? 2
  • +
  • -

#10 jink  Icon User is offline

  • D.I.C Head

Reputation: 5
  • View blog
  • Posts: 63
  • Joined: 10-July 11

Re: Imitating printf function , getting output wrong

Posted 30 June 2012 - 06:50 AM

my code gave correct output.thank you for your help.

Quote

Just because a piece of code works fine on its own doesn't mean it won't cause problems when interacting with other parts of the code.

I hadn't realised this until now.increased my understanding.

Quote

always make sure that the code you post to a forum actually compiles and produces the same error as your actual code.
it was compiling and producing no error and it was giving same output as i had posted.i wasn't getting the 2 errors that you showed me,even when i pasted the functions in one file and compiled them.

Quote

the chance that people will bother to debug your code for you is much smaller than if they can just copy and paste your code and it runs.

sorry.i see now that its easy for people if entire code is available.will ensure it in future.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1