Giant Number - Three Operations (+,-,*)

Posted by Mantis_61 on Mon, 12 Aug 2019 10:08:20 +0200

This blog article mainly explains an idea - converting strings to values, then to structures, and reviewing the use of some file operations.
Then, return to the theme:

As far as we know, the maximum positive number that an int variable can represent is more than 2.1 billion. So, if we want to process data much larger than 2.1 billion, how do we achieve it? Because other types also have extreme values, if they break through the limits, mistakes will occur. There's no reason to say that. Let's try with vc6.0.

As you can see, I input 2.2 billion, but output is not 4.3 billion. So, it can be seen that int variables have limits, so in order to solve this problem, we will write a program to operate on any legitimate data and output it accurately.

At first, we can not complete decimal operation and division operation because of lack of ability. Then, we will carry out integer operation, and then advance to decimal. As for division, the logic is too complicated to proceed.

Integer:

In this paper, I use the method of storing data in the decimal system (Professor of Weiyi Code).
So why use the decimal system? Here are some explanations:
Firstly, the maximum storage range of ints (i.e. integer variables) is 0-2.1 billion or so, that is, the 9th place, while Wan is the 4th place. The maximum result of multiplying two tens of thousands of numbers is (4+4) or 8 places, which will not cause errors.
Secondly, millions store "huge numbers" and waste a little less space when the first item is satisfied.

Now, let's start writing the program:
First, since we are going to use the decimal system, we first define a decimal system structure, and we define the decimal system structure, each decimal number in the structure, we need to know its digits, the value of each bit, the positive and negative sign of the number, the code is as follows:

typedef struct HUGE {
	int digit;
	int *num;
	SIGN sign;
}HUGE;

Here, for convenience, we define a new type of SIGN, which has only two values: positive and negative. The code is as follows:

typedef unsigned char SIGN;
#define PLUS 0 // Definition is 0
#define MINUS 1 // Define negative 1

Secondly, now we write the function of input huge numbers. Here, I will put forward some requirements and realization ideas about this function.
We need to save documents, and in the document you can directly see the huge number of deposits, and this huge number, typesetting is very regular, beautiful, so we will use the knowledge of the document there, for students who do not understand here, you can refer to my previous blog "Document Operation". As for typesetting, it's easier to save a few numbers for a space and a few numbers for a carriage return. Also, the most critical point is the valid value stored in the file, so we need to filter the value entered by the user, the code is as follows:

void inputFile(char *name) {    //The parameter here is the file name, which is used to enter a "huge number" into the specified file (or to generate the specified file).
	char ch;          //Values for receiving keyboard input
	int flag=1;       //Used to determine whether the loop continues
	int count=0;      //The number of digits used to record valid data for subsequent typesetting problems

	FILE *p;          //Used to open and close files

	p=fopen(name, "w");
	printf("\n Please enter the data to be entered: (Enter)#Stop typing) n "; 

	ch=getch();       //The function of a getch is to receive the characters entered by the keyboard, and if the keyboard does not enter, wait until the input before proceeding to the next line of code.
	while(flag) {     //Here, flag variables are used to determine whether the loop continues or not.
		if(ch == '-') {
			printf("-");        //Echo the characters of the input file
			fputc(ch, p);		//The function of fputc is to enter the location of the file pointed to by p in character form.
		} else if(ch <= '9' && ch >= '0') {
			printf("%c", ch);   //Echo the characters of the input file
			fputc(ch, p);       //Refer to the explanation above.
			count++;            //Valid digits plus one (so-called valid digits, that is, the number of 0-9)
			flag=0;             //If the condition is satisfied, jump out of the loop (the purpose of this loop is to process the first valid character)
		} else if(ch == '#') {  //If the first valid character is #, enter 0 and end the function
			printf("0"); 
			fputc('0', p);
			return;
		} else if(ch == '+') {   //Positive sign is not entered, only echoed, which can slightly reduce the amount of code for the next read function.
			printf("+");
		}
		ch=getch();              //If a valid value has not been entered, let the user continue to enter it.
	}                            //The purpose of this loop is to process the first valid value (i.e. characters 0-9)

	while(ch != '#') {
		if(ch <= '9' && ch >= '0') {
			printf("%c", ch);
			fputc(ch, p);
			count++;
		}
		if(0 == count%4) {          //This judgment is for typesetting (even if the data stored in the file is intuitive)
			printf(" ");
			fputc(' ', p);
		} else if(0 == count%16) {
			fputc(13, p);
			printf("\n");
		}
		ch=getch();
	}
	fclose(p);
}

3. Because there are huge numbers that need to be raised from the document before they can be operated, we write a function in this step to put the numbers in the document into the structure. The code is as follows:

HUGE getNumFromFile(char *fileName) {       //Return value: Because the "data" stored in the file is put into the structure, the return value is HUGE type. About parameters: refer to the parameters of the previous function
	int i;
	int j;
	char ch;

	HUGE num1={0, NULL, PLUS};     //Here is the initialization of the defined structure to be returned
	
	FILE *fp;

	fp=fopen(fileName, "r");
	rewind(fp);                             //The rewind function points fp to the beginning of the file
	if(NULL == fp) {                        //Here if is used to determine whether there is data in the file, to prevent incoming file name is not entered into the data file.
		printf("\nERROR!!!\n");
		return;
	}
	ch=fgetc(fp);
	if(ch == '-') {                         //The judgment here is related to the above entry. When entering, only negative sign is entered, no positive sign is entered, and when there is no sign, positive number is defaulted to be positive.
		num1.sign=MINUS;
	} else {
		num1.sign=PLUS;
	}
	while((EOF != ch) {
		if(ch>='0' && ch<='9') {
			num1.digit++;
		}
		ch=fgetc(fp);
	}                                       //This loop is used to determine the number of valid digits
	
	rewind(fp);                             //The effect is similar to that mentioned above.
	num1.num=(int *)calloc(sizeof(int), (num1.digit+3)/4+1);          //This function is included in the <malloc.h> library, which is used to apply for space for the "universal array" in the structure. Here, an additional space is applied for the purpose of defining the methods to be used for the next addition.
	ch=fgetc(fp);
	for(j=0; j<(num1.digit%4); j++) {                                 //We have low bits in cells with small array subscripts. Here is the assignment of the last unit, because the last unit, the highest bit, is uncertain, so here we first determine the value of the last unit.
		if(ch>='0' && ch<='9') {
			num1.inum[num1.digit+3)/4-1]=num1.num[num1.digit+3)/4]*10+(ch-'0'); //Explain the meaning of "ch-'0'": because the character is stored in the file, which is also ASCII code when it is regarded as a value, the character 0 is subtracted from the character, which is the corresponding value.
		} else {
			j--;       //If the blank space and return line are changed, no entry is made. This step can be regarded as pausing the cycle.
		}
		ch=fgetc(fp);
	}
	for(i=(num1.digit+3)/4-2; i>=0; i--) {                           //Except for the last unit, all the other units are four-bit.
		for(j=0; j<4; j++) {
			if(ch>='0' && ch<='9') {
				num1.num[i]=num1.num[i]*10+(ch-'0');
			} else {
				j--;
			}
			ch=fgetc(fp);
		}
	}
	
	fclose(fp);
	return num1;	
}

Fourthly, all the results we get are converted to the form of structure. So, here we begin to write functions that display huge numbers through the structure. The code is as follows:

void showNum(HUGE num1) {
	int i;
	int count=0;

	if(num1.sign == MINUS) {
		printf("-");
	}
	for(i=(num1.digit+3)/4-1; i>=0; i--) {
		if(!count) {                   //This judgment is used to distinguish whether the highest bit output is the highest bit output. The highest bit array unit may not be four bits, but the non-highest bit array unit is four bits.
			printf("%d", num1.num[i]);
			count++;
		} else {
			printf("%04d", num1.num[i]);
		}
	}
}

Now let's talk about the basic principles of addition, subtraction and multiplication.
I. Addition and subtraction:
(Addition and subtraction can actually be regarded as the same kind, as long as the sign of subtraction is changed, it is the addition of two numbers, so here we will put the addition and subtraction together.)
For the decimal system we use, the addition is somewhat different, because each "unit" is independent, but carry can be achieved by a variable, as long as the variable is used and then assigned to zero.
However, here, I would like to explain a "very strange" processing method - micro-easy code complement:
The so-called "Weiyi Code Complement" is summarized and deduced by the tutor of Weiyi Code. This method is not very good for me, or can not be deduced.
So I will not deduce here, but will tell the conclusion directly:
In our usual study, we may learn about the original code and complement knowledge. If you have not known before, you should not worry about it. As for the original code and complement related content.
I will also explain in future blog articles.

The relationship between the original code and the complement we have known is:

Positive complement - positive source code itself;
Complement of Negative Number - In addition to the symbol bit, reverse by bit, last bit + 1 (This simple algorithm can be deduced from the relationship)

So, the so-called "micro-easy code complement" is a good way to deal with the problem of positive plus negative numbers.
So that in the process of adding, there will be no problem of subtraction because of symbols.
Here's how to use it:

  1. Complement of Positive Number-Positive Number itself
    Negative complement - each bit takes 9 minus the value of that bit (because the original complement is based on the binary level, and our operation is for decimal numbers)
  2. If the sum of each corresponding unit is greater than 9999, carry it, and take the number of the unit and 10000 (or subtract 10000 directly). Then according to (symbols equal)? 0:1) ^ (top carry? 1:0)
    If the result is 1, each bit plus one, the sign is negative; if the result is 0, each bit is not added, and the sign is positive.
  3. Finally, if it is a negative sign, then the value of each of you is subtracted by 9 (similar to the process of converting the complement of a negative number into the original code).

Here is the code for the addition and subtraction function:

HUGE addNum(HUGE num1, HUGE num2) {
	int count;
	int i;
	int carry=0;
	int *p=NULL;
	int temp;

	HUGE num3={0, NULL, PLUS};

	count=num1.digit>num2.digit ? num1.digit : num2.digit;
	num3.num=(int *)calloc(sizeof(int), (count+3)/4+1);
	if(MINUS == num1.sign) {                //Here is the transformation of negative numbers into "micro-commutative code complement"
		num1.num[(num1.digit+3)/4]=9999;    //Since we define more of the previous int-type space, we convert this space first.
		for(i=0; i<(num1.digit+3)/4; i++) {
			num1.inum[i]=9999-num1.inum[i];
		}
	} else {
		num1.num[(num1.digit+3)/4]=0;
	}
	if(MINUS == num2.sign) {
		num2.num[(num2.digit+3)/4]=9999;
		for(i=0; i<(num2.digit+3)/4; i++) {
			num2.num[i]=9999-num2.num[i];
		}
	} else {
		num2.num[(num2.digit+3)/4]=0;
	}
	for(i=0; i<(num1.idigit<num2.idigit ? (num1.idigit+3)/4+1  : (num2.idigit+3)/4) +1; i++) {
		temp=num1.num[i]+num2.num[i]+carry;
		num3.num[i]=temp%10000;
		carry=(temp>10000 ? 1 : 0);
	}
	p=(num1.digit>num2.digit ? num1.num : num2.num);   //Save the number of digits in an array for the following operations
	for(;i<(num1.digit>num2.digit ? (num1.digit+3)/4 + 1: (num2.digit+3)/4) + 1; i++) {
		temp=p[i]+carry;
		num3.num[i]=temp%10000;
		carry=(temp>10000 ? 1 : 0);
	}
	if(num1.sign == num2.sign) {
		num3.sign=num1.sign;
	} else {
		num3.sign=1^carry;
	}
		num3.num[i]%=10000;
	}
	if(MINUS == num3.sign) {
		for(i = 0; i <= (num3.digit+3)/4+1; i++) {
			num3.num[i] = 9999 - num3.num[i];
		}
	}
	count=((count+3)/4)*4;
	for(i=(count+3)/4; i>=0 && !num3.num[i]; i--) {
		count-=4;
	}
	if(!(num3.num[i]/10)) {
		count-=3;
	} else if(!(num3.num[i]/100)) {
		count-=2;
	} else if(!(num3.num[i]/100)) {
		count-=1;
	}
	num3.digit=count;
	return num3;
}

HUGE subNum(HUGE num1, HUGE num2) {
	HUGE num4={PLUS, NULL, 0};

	num2.sign = num2.sign==PLUS ? MINUS : PLUS;
	num4=addNum(num1, num2);
	
	return num4;
}

II. Multiplication:
For multiplication, it is much simpler than addition, because the essence of multiplication can be seen as polynomial multiplication. In the decimal system, the subscripts of each unit are the units of the number of units in which the original number is 10,000.
So the code is as follows:

HUGE multyNum(HUGE num1, HUGE num2) {
	int i;
	int j;
	int count;

	HUGE num5={PLUS, NULL, 0};

	num5.num=(int *)calloc(sizeof(int), (num1.digit+num2.digit+2)/4+1);

	for(i=(num1.digit+3)/4-1; i>=0; i--) {
		for(j=(num2.digit+3)/4-1; j>=0; j--) {
			num5.num[i+j]+=(num1.num[i]*num2.num[j])%10000;
			num5.num[i+j+1]+=(num1.num[i]*num2.num[j])/10000;
		}
	}

	count=(num1.digit+num2.digit+6)/4*4;

	for(i=(num1.digit+num2.digit+2)/4; i>=0 && !num5.num[i]; i--) {
		count-=4;
	}
	if(!(num5.num[i]/10)) {
		count-=3;
	} else if(!(num5.num[i]/100)) {
		count-=2;
	} else if(!(num5.num[i]/100)) {
		count-=1;
	}
	num5.digit=count;

	return num5;
}

This blog is written in the first summer vacation of the NPC. If you have any comments or suggestions, please actively put forward, and I will reply as soon as possible!!!

Topics: less ascii