总体原则:
字符串转整形数组,然后按照“竖式计算”的思想,按位(对于数组来说,就是对应位置的元素)进行运算,同时处理进位、退位。最后将整形数组转换为字符串输出。
Ps:1、字符串转整形,本文采取逆序存储的方式,即将字符串的低位(大数的高位)放置到整形数组的高位。
2、本文提供的四个四则运算方法,所有的输入值(大数)必须为正整数。
一、加法
加法运算遵循从低位到高位运算的法则。将字符串转换为整形数组后,两数组对应元素相加,结果存储至结果数组的相应元素位置。同时对相加后的元素进行整除和取余运算(整除结果即进位,取余结果即本位置最终加和结果),将结果存至相应位置。
注意:考虑到高位可能存在进位的情况,用于保存结果的数组大小应比两数最大长度大1。
/////////////////////////////////////////////////////////////////////////////// // // NAME: Add // // DESCRIPTION: An operation for two large numbers adding // // PARAMETERS: stNumA/strNumB - the input numbers // strResult - the result of addtion // lenRes - the length of ‘result‘ // // RETURN: int - indicates the operation success or failure // 0 failure // 1 success /////////////////////////////////////////////////////////////////////////////// int Add(char* strNumA, char* strNumB, char* strResult, int lenRes) { int lenA = strlen(strNumA); int lenB = strlen(strNumB); //Check user‘s input if((lenA <= 0 || lenA > MAX_LENGTH) || (lenB <= 0 || lenB > MAX_LENGTH)) return 0; int len = lenA; while(len--) { if(strNumA[len] > 57 || strNumA[len] < 48) return 0; } len = lenB; while(len--) { if(strNumB[len] > 57 || strNumB[len] < 48) return 0; } int arNumA[MAX_LENGTH] = {0}; int arNumB[MAX_LENGTH] = {0}; int arRes[MAX_LENGTH] = {0}; //Convert string to array for(int i = 0; i < lenA; i++) arNumA[i] = strNumA[lenA-i-1] - ‘0‘; for(int i = 0; i < lenB; i++) arNumB[i] = strNumB[lenB-i-1] - ‘0‘; while(arNumA[lenA-1] == 0 && lenA > 1) lenA--; while(arNumB[lenB-1] == 0 && lenB > 1) lenB--; //Get the max length of input number int lenMax = lenA > lenB ? lenA:lenB; lenMax++; //Calculate the numbers digit by digit for(int i = 0; i < lenMax; i++) { arRes[i] += arNumA[i] + arNumB[i]; //Deal with carries arRes[i+1] += arRes[i]/10; arRes[i] %= 10; } //Re-calculate the length of result while(arRes[lenMax-1] == 0 && lenMax > 1) lenMax--; //Convert array to string if(lenMax+1 > lenRes) return 0; for(int i = 0; i < lenMax; i++) { strResult[i] = arRes[lenMax-i-1] + ‘0‘; } strResult[lenMax] = ‘\0‘; return 1; }
二、减法
减法运算遵循从低位到高位运算的法则。将字符串转换为整形数组后,两数组对应元素相减,结果存储至结果数组的相应元素位置。同时对相减后的结果进行判断,如果大于0,将结果存至相应位置;如果小于0,结果加10后存入相应位置,同时高位元素置1,表示存在借位。这样高位相减的时候,多减1,相当于借了10。
注意:两数相减之前,需判断大小,要用大的数减去小的数,即减数与被减数交换。如果存在交换,在输出的时候,结果前面需要加”-“号。
/////////////////////////////////////////////////////////////////////////////// // // NAME: Sub // // DESCRIPTION: An operation for two large numbers subtracting // // PARAMETERS: stNumA/strNumB - the input numbers // strResult - the result of subtraction // lenRes - the length of ‘result‘ // // RETURN: int - indicates the operation success or failure // 0 failure // 1 success /////////////////////////////////////////////////////////////////////////////// int Sub(char* strNumA, char* strNumB, char* strResult, int lenRes) { int lenA = strlen(strNumA); int lenB = strlen(strNumB); //Check user‘s input if((lenA <= 0 || lenA > MAX_LENGTH) || (lenB <= 0 || lenB > MAX_LENGTH)) return 0; int len = lenA; while(len--) { if(strNumA[len] > 57 || strNumA[len] < 48) return 0; } len = lenB; while(len--) { if(strNumB[len] > 57 || strNumB[len] < 48) return 0; } int arNumA[MAX_LENGTH] = {0}; int arNumB[MAX_LENGTH] = {0}; int arRes[MAX_LENGTH] = {0}; //Convert string to carries for(int i = 0; i < lenA; i++) arNumA[i] = strNumA[lenA-i-1] - ‘0‘; for(int i = 0; i < lenB; i++) arNumB[i] = strNumB[lenB-i-1] - ‘0‘; while(arNumA[lenA-1] == 0 && lenA > 1) lenA--; while(arNumB[lenB-1] == 0 && lenB > 1) lenB--; int ret = CompareNum(arNumA, lenA, arNumB, lenB); int sign = 0; //Calculate the numbers digit by digit. Three situation should be concerned: A>B;A<B;A=B switch(ret) { case 1: sign = 1; for(int i = 0; i < lenA; i++) { int diff = arNumA[i] - arNumB[i] - arRes[i]; arRes[i] = diff; if(diff >= 0) arRes[i] = diff; else { arRes[i] += 10; arRes[i+1] = 1; } } break; case -1: sign = 0; for(int i = 0; i < lenB; i++) { int diff = arNumB[i] - arNumA[i] - arRes[i]; arRes[i] = diff; if(diff < 0) { arRes[i] += 10; arRes[i+1] = 1; } } break; case 0: sign = 1; break; } int lenMax = lenA>lenB?lenA:lenB; //Re-calculate the length of result while(arRes[lenMax-1] == 0 && lenMax > 1) lenMax--; if(lenMax + 2 > lenRes) return 0; //Convert array to string if(sign == 0) { strResult[0] = ‘-‘; for(int i = 1; i < lenMax+1; i++) { strResult[i] = arRes[lenMax-i] + ‘0‘; } strResult[lenMax+1] = ‘\0‘; } else { for(int i = 0; i < lenMax; i++) { strResult[i] = arRes[lenMax-i-1] + ‘0‘; } strResult[lenMax] = ‘\0‘; } return 1; } /////////////////////////////////////////////////////////////////////////////// // // NAME: CompareNum // // DESCRIPTION: Compare the two input numbers // // PARAMETERS: arNumA/arNumB - the input numbers // lenA/lenB - the length of numbers // // RETURN: int - indicates the result of comparison // 1 A > B // -1 A > B // 0 A = B /////////////////////////////////////////////////////////////////////////////// int CompareNum(int* arNumA, int lenA, int* arNumB, int lenB) { if(lenA > lenB) return 1; else if(lenA < lenB) return -1; else { while(lenA > 0) { if(arNumA[lenA-1] == arNumB[lenA-1]) lenA--; else if(arNumA[lenA-1] > arNumB[lenA-1]) return 1; else return -1; } return 0; } }
三、乘法
乘法的运算逻辑与”竖式计算“一致,即一个数的每一位分别乘以另一个数的每一位,然后将计算结果按照一定的规律加和,乘法的难点就是在于这个加和的规律。通过观察和总结可发现,A数的第i个元素与B数的第j个元素所乘得的结果存放在结果数组的第i+j个元素中。按照这个规律,乘法的运算可以总结为:按位相乘,结果存入结果数组的对应元素(其中存在累加,所以存入时要使用+=运算),同时与大数加法的逻辑一样,处理进位问题。
/////////////////////////////////////////////////////////////////////////////// // // NAME: Mul // // DESCRIPTION: An operation for two large numbers multiplying // // PARAMETERS: strNumA/strNumB - the input numbers // strResult - the result of multiply // lenRes - the length of ‘result‘ // // RETURN: int - indicates the operation success or failure // 0 failure // 1 success /////////////////////////////////////////////////////////////////////////////// int Mul(char* strNumA, char* strNumB, char* strResult, int lenRes) { int lenA = strlen(strNumA); int lenB = strlen(strNumB); //Check user‘s input if(lenA < 0 || lenA > MAX_LENGTH || lenB < 0 || lenB > MAX_LENGTH) return 0; int len = lenA; while(len--) { if(strNumA[len] > 57 || strNumA[len] <48) return 0; } len = lenB; while(len--) { if(strNumB[len] > 57 || strNumB[len] <48) return 0; } int arNumA[MAX_LENGTH] = {0}; int arNumB[MAX_LENGTH] = {0}; int arRes[MAX_LENGTH*2] = {0}; //Convert string to array for(int i = 0; i < lenA; i++) arNumA[i] = strNumA[lenA-1-i] - ‘0‘; for(int i = 0; i < lenB; i++) arNumB[i] = strNumB[lenB-1-i] - ‘0‘; while(arNumA[lenA-1] == 0 && lenA > 1) lenA--; while(arNumB[lenB-1] == 0 && lenB > 1) lenB--; //Calculate digit by digit for(int i = 0; i < lenA; i++) for(int j = 0; j < lenB; j++) { arRes[i+j] += arNumA[i] * arNumB[j]; //Deal with carries arRes[i+j+1] += arRes[i+j]/10; arRes[i+j] %= 10; } int lenMax = lenA + lenB; //Re-calculate the length of result while(arRes[lenMax-1] == 0 && lenMax > 1) lenMax--; if(lenMax+1 > lenRes) return 0; //Convert array to string for(int i = 0; i < lenMax; i++) { strResult[i] = arRes[lenMax-i-1] + ‘0‘; } strResult[lenMax] = ‘\0‘; return 1; }
四、除法
除法运算相较前面三种运算,实现起来稍稍麻烦一些,也和”竖式计算“的思想有一些小的出入。大体说来,除法的运算的思想就是用被除数去减除数,减的次数即为商,剩下不够减的,即为余数。但是对于大数运算来说,单纯的循环减法,计算次数,是不符合大数运算要求的(次数,即商,也可能是大数),所以需要一些特殊的处理。具体处理方法为:对于除数大于或等于被除数的情况,结果很容易得出,不啰嗦;对于除数小于被除数的情况,首先将除数扩大n倍,使其与被除数长度一致,然后循环做减法,减的次数×扩大的倍数N,为结果数组第N个元素的值。接下来除数扩大N-1倍,用之前剩下的余数循环做减法,减的次数×扩大的倍数N-1,为结果数组第N-1个元素的值。按此循环,的到最终的商,余下的值则为余数。
举例:258/4 --> 258 - 400 --> 0 余 258 --> 258 - 40 --> 6 余 18 --> 18 - 4 --> 4 余 2
按照之前的对应原则将商组合,的到结果:商64;余2
/////////////////////////////////////////////////////////////////////////////// // // NAME: Div // // DESCRIPTION: An operation for two large numbers dividing // // PARAMETERS: strNumA/strNumB - the input numbers // strResult/strRemainder - the result of division // lenRes/lenRem - the length of ‘result‘ // // RETURN: int - indicates the operation success or failure // 0 failure // 1 success /////////////////////////////////////////////////////////////////////////////// int Div(char* strNumA, char* stNumB, char* strResult, int lenRes, char* strRemainder, int lenRem) { int lenA = strlen(strNumA); int lenB = strlen(stNumB); //Check user‘s input if(lenA < 0 || lenA > MAX_LENGTH || lenB < 0 || lenB > MAX_LENGTH) return -1; int len = lenA; while(len--) { if(strNumA[len] > 57 || strNumA[len] <48) return -1; } len = lenB; while(len--) { if(stNumB[len] > 57 || stNumB[len] <48) return -1; } int arNumA[MAX_LENGTH] = {0}; int arNumB[MAX_LENGTH] = {0}; int arRes[MAX_LENGTH] = {0}; int arRemainder[MAX_LENGTH] = {0}; //Convert string to array for(int i = 0; i < lenA; i++) arNumA[i] = strNumA[lenA-1-i] - ‘0‘; for(int i = 0; i < lenB; i++) arNumB[i] = stNumB[lenB-1-i] - ‘0‘; while(arNumA[lenA-1] == 0 && lenA > 1) lenA--; while(arNumB[lenB-1] == 0 && lenB > 1) lenB--; int qlen = lenA; int rlen = lenB; int ret = CompareNum(arNumA, lenA, arNumB, lenB); int times = lenA - lenB; switch(ret) { case 1: if(times > 0) { int i = 0; for(i= lenA - 1; i >= times; --i) arNumB[i]=arNumB[i-times]; //Move to high position for(;i >= 0; --i) arNumB[i] = 0; //Cover 0 to low positon lenB = lenA; } for(int i = 0; i <= times; i++) { while(SubForDiv(arNumA, arNumB + i, lenA, lenB)) { arRes[times-i]++; } } memcpy(arRemainder, arNumA, rlen* sizeof(int)); break; case -1: qlen = 1; rlen = lenB; memcpy(arRemainder, arNumB, rlen* sizeof(int)); break; case 0: qlen = 1; rlen =1; arRes[0]=1; break; } //Re-calculate the length of result while(arRes[qlen-1] == 0 && qlen > 1) qlen--; while(arRemainder[rlen-1] == 0 && rlen > 1) rlen--; if(qlen+1 > lenRes || rlen+1 > lenRem) return -1; //Convert array to string for(int i = 0; i < qlen; i++) strResult[i] = arRes[qlen-i-1] + ‘0‘; strResult[qlen] = ‘\0‘; for(int i = 0; i < rlen; i++) strRemainder[i] = arRemainder[rlen-i-1] + ‘0‘; strRemainder[rlen] = ‘\0‘; return 1; } /////////////////////////////////////////////////////////////////////////////// // // NAME: SubForDiv // // DESCRIPTION: An operation for two large numbers subtracting,the result assign to arNumA. // // PARAMETERS: arNumA/arNumB - the input numbers // lenA/lenB - the length of the two numbers // // RETURN: int - indicates the operation success or failure // 0 failure // 1 success /////////////////////////////////////////////////////////////////////////////// int SubForDiv(int* arNumA, int* arNumB, int lenA, int lenB) { while(arNumA[lenA-1] == 0 && lenA > 1) lenA--; while(arNumB[lenB-1] == 0 && lenB > 1) lenB--; int ret = CompareNum(arNumA, lenA, arNumB, lenB); int res = 0; int arRes[MAX_LENGTH] = {0}; //Calculate the numbers digit by digit. Three situation should be concerned: A>B;A<B;A=B switch(ret) { case 1: for(int i = 0; i < lenA; i++) { int diff = arNumA[i] - arNumB[i] - arRes[i]; arRes[i] = diff; if(diff >= 0) arRes[i] = diff; else { arRes[i] += 10; arRes[i+1] = 1; } } memcpy(arNumA, arRes, lenA*sizeof(int)); res = 1; break; case -1: res = 0; break; case 0: memset(arNumA, 0, lenA * sizeof(int)); res = 1; break; } return res; } /////////////////////////////////////////////////////////////////////////////// // // NAME: CompareNum // // DESCRIPTION: Compare the two input numbers // // PARAMETERS: arNumA/arNumB - the input numbers // lenA/lenB - the length of numbers // // RETURN: int - indicates the result of comparison // 1 A > B // -1 A > B // 0 A = B /////////////////////////////////////////////////////////////////////////////// int CompareNum(int* arNumA, int lenA, int* arNumB, int lenB) { if(lenA > lenB) return 1; else if(lenA < lenB) return -1; else { while(lenA > 0) { if(arNumA[lenA-1] == arNumB[lenA-1]) lenA--; else if(arNumA[lenA-1] > arNumB[lenA-1]) return 1; else return -1; } return 0; } }
结语:
以上大数的四则运算是我看过的一些其他人的实现,然后根据自己的理解总结的。写的看上去比较冗长,是因为我加了一些输入验证的判断。此外有些代码可以提炼出函数,但是为了看起来方便,逻辑连贯,我没有将他们单独提出来。所有函数我都经过简单的测试,至少没有语法错误~。如果大家有什么更好的建议或者发现什么问题,欢迎随时联系,一起交流,共同进步~