[模板]高精度数字加减乘除以及大小比较
本文中所有高精度数字均使用char[]进行初次存储,而不是string类.
一.高精除以低精(带余数)
举例来说:
12345除以13.
想象一下手算过程,把里面的细节都挖出来:
00949
13√ 12345
117
64
52
125
117
8
1除以13,不够 .上写0.余 1,结合下一位变为12.
12除以13,不够.上写0.余 12,结合下一位变为123.
123除以13,可以,上写9,下写117.123-117余6,结合原数下一位变为64.
64除以13,上写4,下写52,64-52余12,结合原数下一位变为125.
125除以13,上写9,下写117,125-117余8.
这个过程里面所谓"结合下一位"即把当前余数乘以10再加上下一位的数字.
注意到这个过程是从最左边一位开始的,所以代码里面也从最高位开始计算,注意这里位置的几次翻转.
输出的时候要跳过前导0.注意这里除了字符串数组外下标视为从1开始.结果为0时需要特判.
#include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> using namespace std; char A[1010]; int B, A_int[1010], ans[1010]; int main() { scanf("%s%d", A, &B); int len = strlen(A); for (int i = len - 1; i >= 0; i--) A_int[len - i] = A[i] - '0'; for (int i = len; i >= 1; i--) { ans[i] = A_int[i] / B; A_int[i - 1] += (A_int[i] % B) * 10; } while (!ans[len] && len) len--; if(!len) putchar('0'); for (int i = len; i >= 1; i--) printf("%d", ans[i]); // 如果余数保证是个位数,那么余数即为A_int[0] / 10 return 0; }
#include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> using namespace std; char A[1010]; int B, A_int[1010], ans[1010]; int div1(int x, int l) { // 将 A_int[] 除以x, l 为A_int[]的长度 int tmp[1010]; A_int[0] = 0; for (int i = l; i >= 1; i--) { tmp[i] = A_int[i] / x; A_int[i - 1] += (A_int[i] % x) * 10; } while (!tmp[l] && l) l--; for (int i = l; i >= 1; i--) A_int[i] = tmp[i]; return l; // 返回处理后A_int[]的长度, 结果为0时返回0 } int main() { scanf("%s%d", A, &B); int len = strlen(A); for (int i = len - 1; i >= 0; i--) A_int[len - i] = A[i] - '0'; // 预处理 len = div1(B, len); if (!len) putchar('0'); for (int i = len; i >= 1; i--) printf("%d", A_int[i]); printf("\n%d\n", A_int[0] / 10); // 余数 return 0; }
二.高精度乘以高精度/低精度
(当然,对高精度乘以低精度也适用)
还以12345x13为例,我修改了手算方法,但没有改变本质:
1 2 3 4 5
x 1 3
3 6 9 12 15
1 2 3 4 5
1 5 9 13 17 15
↓(进位)
结果:160485
从13的最低位"3"开始,用3与从12345的最低位"5"开始的每个数字逐个相乘,并从"3"正下方开始向左依次写下结果.先不进位,留到最后再进位是不会影响最终结果的.
然后,再用13的高一位"1"开始,逐个相乘,从"1"正下方开始向左依次写下结果.
最后,把上述两行结果相加,之后进位得答案.
注意位数是不会影响结果的,可以视为12345x00013.
结果为0时需要特判.
#include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> using namespace std; char A[1010], B[1010]; int A_int[1010], B_int[1010], ans[1010]; int main() { scanf("%s%s", A, &B); int lenA = strlen(A), lenB = strlen(B); for (int i = lenA - 1; i >= 0; i--) A_int[lenA - i] = A[i] - '0'; for (int i = lenB - 1; i >= 0; i--) B_int[lenB - i] = B[i] - '0'; for(int i = 1; i <= lenB; i++) for(int j = 1; j <= lenA; j++) ans[j + i - 1] += B_int[i] * A_int[j]; for(int i = 1; i <= lenA + lenB; i++){ ans[i + 1] += ans[i] / 10; ans[i] %= 10; } int head = lenA + lenB; while(!ans[head] && head) head--; if(!head) puts("0"); for(int i = head; i >= 1; i--) printf("%d", ans[i]); return 0; }
#include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> using namespace std; char A[1010]; int A_int[1010], B; int mul(int x, int l) { int tmp[1010]; for (int i = 1; i <= l; i++) tmp[i] = A_int[i] * x; l = 0; for (int i = 1; tmp[i]; i++, l++) { tmp[i + 1] += tmp[i] / 10; tmp[i] %= 10; } for (int i = 1; i <= l; i++) A_int[i] = tmp[i]; return l; } int main() { scanf("%s%d", A, &B); int len = strlen(A); for (int i = len - 1; i >= 0; i--) A_int[len - i] = A[i] - '0'; len = mul(B, len); if (!len) puts("0"); for (int i = len; i >= 1; i--) printf("%d", A_int[i]); if (len) putchar('\n'); return 0; } 高精度乘以低精度_函数化
三.高精度加法
(这里不考虑负数)
最简单的高精度运算,将两个数字补0后视为长度相等.之后对每一位有:
ans[i] = A_int[i] + B_int[i];
在此之后进位处理一下即可,结果为0时需要特判.
#include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> using namespace std; char A[1010], B[1010]; int A_int[1010], B_int[1010], ans[1010]; int main() { scanf("%s%s", A, B); int lenA = strlen(A), lenB = strlen(B); int lenAns = max(lenA, lenB) + 1; for (int i = lenA - 1; i >= 0; i--) A_int[lenA - i] = A[i] - '0'; for (int i = lenB - 1; i >= 0; i--) B_int[lenB - i] = B[i] - '0'; for(int i = 1; i <= lenAns; i++) ans[i] = A_int[i] + B_int[i]; for(int i = 1; i <= lenAns; i++){ ans[i + 1] += ans[i] / 10; ans[i] %= 10; } while(!ans[lenAns] && lenAns) lenAns--; if(!lenAns) puts("0"); // 特判 for(int i = lenAns; i >= 1; i--) printf("%d", ans[i]); return 0; }
四.高精度数字的大小比较
(在此基础上,之后讨论高精度减法)
int hpCmp(char *a, char *b) { // 若 a > b, 返回1, 若 a < b, 返回-1, 若 a == b, 返回0, 这里a, b是转化为数字之前的高精度数字(即输入的字符串) int lenA = strlen(a), lenB = strlen(b); if (lenA == lenB) return strcmp(a, b); else return lenA > lenB ? 1 : -1; }
这样写的原因是显然的.
五.高精度减法
这里只写出较大正整数减去较小正整数的方法,在此基础上通过对符号的提取总可以将任意的高精度减法问题转化为此种情况的运算和高精度加法运算.可以以这题为例.
大数减小数可以以如下规则进行:
ans[i] = A_int[i] - B_int[i];
显然ans中会出现负数位,这意味着原本是应该向高一位借1的,但在算出所有位之后再处理这些"借1"是不会影响结果的,这与加法相似.
for (int i = 1; i <= lenAns; i++) if(ans[i] < 0){ ans[i] += 10; ans[i + 1]--; }
这里的代码输出你所输入两数差的绝对值.
#include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> using namespace std; char A[1010], B[1010]; int A_int[1010], B_int[1010], ans[1010]; int hpCmp(char *a, char *b) { int lenA = strlen(A), lenB = strlen(B); if (lenA == lenB) return strcmp(a, b); else return lenA > lenB ? 1 : -1; } int main() { scanf("%s%s", A, B); if (hpCmp(A, B) == -1) swap(A, B); int lenA = strlen(A), lenB = strlen(B); int lenAns = max(lenA, lenB); for (int i = lenA - 1; i >= 0; i--) A_int[lenA - i] = A[i] - '0'; for (int i = lenB - 1; i >= 0; i--) B_int[lenB - i] = B[i] - '0'; for (int i = 1; i <= lenAns; i++) ans[i] = A_int[i] - B_int[i]; for (int i = 1; i <= lenAns; i++) if(ans[i] < 0){ ans[i] += 10; ans[i + 1]--; } int head = lenAns; while(!ans[head] && head) head--; if(!head) puts("0"); for(int i = head; i >= 1; i--) printf("%d", ans[i]); return 0; }