- C++
GESP/信奥专用 C++ 高精度加减乘除 全套竞赛教程
- @ 2026-7-31 20:16:16
GESP/信奥专用 C++ 高精度加减乘除 全套竞赛教程
前言(竞赛考纲定位)
C++ 普通 int、long long 数据类型有固定数值上限,无法存储几十位、上百位的超大整数。高精度算法通过数组模拟人工竖式运算,解决超大整数运算问题,是 GESP4级必考、CSP-J 入门高频考点。
本教程为纯应试版本,无冗余知识点,提供可直接默写的满分模板、统一编码规范、考场避坑要点、经典真题题型,适配所有信奥入门考试。
一、高精度核心基础(必学规范)
1. 为什么要用数组模拟?
int 最大约 2e9(10位),long long 最大约 9e18(19位),竞赛题目常出现 50位、100位、1000位超大整数,原生数据类型无法存储,必须用数组逐位存储数字。
2. 竞赛统一存储规则(重中之重)
所有高精度运算统一采用 低位存在数组下标0 的倒存方式,适配所有加减乘除运算,统一逻辑、减少出错:
- 例:数字 1234,数组存储为:a[0]=4,a[1]=3,a[2]=2,a[3]=1
- 优势:进位、借位从低位开始计算,无需频繁移位,代码极简
3. 通用预处理函数(所有运算通用)
字符串转高精度数组、获取数字长度,所有模板统一调用,考场直接复用:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
const int N = 1005; // 适配1000位以内大数,GESP、CSP-J足够
// 字符串转高精度数组(倒存)
void strToInt(string s, int a[], int &len)
{
len = s.size();
for(int i = 0; i < len; i++)
{
a[i] = s[len - 1 - i] - '0';
}
}
// 输出高精度数组(自动正序输出)
void print(int a[], int len)
{
for(int i = len - 1; i >= 0; i--)
{
cout << a[i];
}
cout << endl;
}
二、高精度加法(GESP 基础必考)
1. 运算原理
模拟竖式加法:从低位到高位逐位相加,保留个位,进位存入下一位,最终处理最高位进位。
2. 考场满分模板
// 高精度加法:a + b,结果存入c
void add(int a[], int la, int b[], int lb, int c[], int &lc)
{
lc = 0;
int carry = 0; // 进位
// 逐位相加,遍历最长数字长度+剩余进位
while(lc < la || lc < lb || carry)
{
int sum = carry;
if(lc < la) sum += a[lc];
if(lc < lb) sum += b[lc];
c[lc] = sum % 10; // 当前位结果
carry = sum / 10; // 更新进位
lc++;
}
}
// 完整可运行示例
int main()
{
string s1, s2;
cin >> s1 >> s2;
int a[N], b[N], c[N];
int la, lb, lc;
strToInt(s1, a, la);
strToInt(s2, b, lb);
add(a, la, b, lb, c, lc);
print(c, lc);
return 0;
}
3. 易错点
- 必须遍历到 进位为0,避免最高位进位丢失
- 无需手动对齐位数,代码自动适配长短数字
三、高精度减法(GESP 进阶必考)
1. 运算规则
仅支持 大减小(竞赛题目默认保证,代码内置比较函数),模拟竖式借位减法,低位不够向高位借1当10。
2. 辅助比较函数(必备)
// 比较两个高精度数大小:a>b返回true,否则false
bool cmp(int a[], int la, int b[], int lb)
{
if(la != lb) return la > lb;
for(int i = la - 1; i >= 0; i--)
{
if(a[i] != b[i]) return a[i] > b[i];
}
return true; // 两数相等
}
3. 考场满分模板
// 高精度减法:a - b (保证a >= b)
void sub(int a[], int la, int b[], int lb, int c[], int &lc)
{
lc = la;
int borrow = 0; // 借位
for(int i = 0; i < lc; i++)
{
// 当前位减借位,再减对应b的位
int now = a[i] - borrow;
if(i < lb) now -= b[i];
if(now < 0) // 不够减,借位
{
now += 10;
borrow = 1;
}
else borrow = 0;
c[i] = now;
}
// 去除前导零(核心!否则输出错误)
while(lc > 1 && c[lc - 1] == 0) lc--;
}
// 完整可运行示例
int main()
{
string s1, s2;
cin >> s1 >> s2;
int a[N], b[N], c[N];
int la, lb, lc;
strToInt(s1, a, la);
strToInt(s2, b, lb);
if(cmp(a, la, b, lb))
{
sub(a, la, b, lb, c, lc);
print(c, lc);
}
else
{
cout << "-";
sub(b, lb, a, la, c, lc);
print(c, lc);
}
return 0;
}
4. 超级易错点
- 减法必须 去前导零,否则会出现 00123 错误输出
- 需要判断大小,负数需手动输出负号
四、高精度乘法(CSP-J 高频)
1. 运算原理
模拟竖式乘法:逐位相乘,错位累加至对应位置,全部计算完成后统一处理进位,相比逐位进位写法更简洁、容错率更高。
2. 考场满分模板
// 高精度乘法:a * b
void mul(int a[], int la, int b[], int lb, int c[], int &lc)
{
// 初始化结果数组为0
fill(c, c + N, 0);
for(int i = 0; i < la; i++)
{
for(int j = 0; j < lb; j++)
{
c[i + j] += a[i] * b[j]; // 错位累加
}
}
// 统一处理进位
lc = la + lb;
for(int i = 0; i < lc; i++)
{
c[i + 1] += c[i] / 10;
c[i] = c[i] % 10;
}
// 去除前导零
while(lc > 1 && c[lc - 1] == 0) lc--;
}
// 完整可运行示例
int main()
{
string s1, s2;
cin >> s1 >> s2;
int a[N], b[N], c[N];
int la, lb, lc;
strToInt(s1, a, la);
strToInt(s2, b, lb);
mul(a, la, b, lb, c, lc);
print(c, lc);
return 0;
}
3. 核心考点
- 两个n位、m位数字相乘,结果最大长度为 n+m
- 先累加、后统一进位,代码更简洁,不易出错
五、高精度除法(GESP4级压轴、CSP-J难点)
竞赛入门仅考 高精度大数 ÷ 低精度整数(高精度÷高精度极少考),提供应试专用模板,支持求商、求余。
1. 考场满分模板(高精度 / 普通整数)
// 高精度除法:a / b(低精度int),返回余数,商存入c
int div(int a[], int la, int b, int c[], int &lc)
{
lc = 0;
int rem = 0; // 余数
// 从高位到低位计算(除法唯一正序遍历)
for(int i = la - 1; i >= 0; i--)
{
rem = rem * 10 + a[i];
c[lc++] = rem / b;
rem = rem % b;
}
// 反转数组,统一低位倒存规则
reverse(c, c + lc);
// 去前导零
while(lc > 1 && c[lc - 1] == 0) lc--;
return rem;
}
// 完整可运行示例
int main()
{
string s;
int b;
cin >> s >> b;
int a[N], c[N];
int la, lc;
strToInt(s, a, la);
int rem = div(a, la, b, c, lc);
print(c, lc);
cout << "余数:" << rem << endl;
return 0;
}
2. 关键特性
- 除法唯一需要 从高位向低位 遍历,与加减乘运算遍历方向相反
- 模板自带余数返回,适配所有求商、求余真题
六、GESP/信奥 考场通用避坑大全
- 存储规则统一:全程低位倒存,禁止混用存储方式,所有模板通用适配
- 前导零必删:减法、乘法、除法必须去前导零,加法无前置零问题,无需处理
- 数组初始化:乘法务必清空数组,避免历史脏数据干扰计算结果
- 边界判断:去前导零时保留最后一位非零数,避免结果0被清空无输出
- 除法特殊逻辑:唯一正序遍历运算,计算后需反转数组,统一全局倒存格式
七、真题适配题型
- GESP3级:高精度大数加法、基础减法运算题
- GESP4级:高精度乘法、大数除法、带余数运算、分数化简
- CSP-J:高精度四则混合运算、大数阶乘、大数幂运算、高精度模拟应用题
2 条评论
-
admin SU @ 2026-7-31 20:17:05
C++ 数组模拟高精度四则运算 零基础完整教程
前置知识说明
为什么需要高精度?
C++ 内置类型:
int、long long存储数字范围有限。long long最大只能存 9e18,超过这个大小就会溢出。 高精度:用数组存储超大整数的每一位,手动模拟竖式加减乘除。统一约定(全部代码遵守)
- 数字逆序存在数组(低位在下,数组下标0存个位)
例:数字
1234→ 数组:a[0]=4, a[1]=3, a[2]=2, a[3]=1✅ 好处:进位直接往后放,不用移动大量元素 len:记录当前数字有效位数- 全部只处理非负大整数(零基础先不学负数,减法单独处理大小比较)
- 数组大小统一开
1005,足够日常练习;需要更大自行调大
逆序示意图 数字: 1 2 3 4 下标: [3][2][1][0] 位值:千 百 十 个
一、高精度加法(A+B)
竖式规则:从个位相加,满10进1
#include <iostream> #include <cstring> using namespace std; const int MAXN = 1005; // 高精度加法 a + b = c void add(int a[], int la, int b[], int lb, int c[], int &lc) { // carry = 进位 int carry = 0; // 最长数字的位数循环 lc = max(la, lb); for(int i = 0; i < lc; i++) { // 当前位 = a当前位 + b当前位 + 进位 int sum = a[i] + b[i] + carry; c[i] = sum % 10; // 当前保留个位 carry = sum / 10; // 算出新进位 } // 如果最后还有进位,多增加一位 if(carry != 0) { c[lc] = carry; lc++; } } // 字符串转逆序数组 void str_to_arr(string s, int num[], int &len) { len = s.size(); for(int i = 0; i < len; i++) { // s[0]是最高位,放到数组尾部,逆序存储 num[len - 1 - i] = s[i] - '0'; } } // 输出逆序数组 void print_arr(int num[], int len) { // 逆序打印,从最高位输出 for(int i = len - 1; i >= 0; i--) cout << num[i]; cout << endl; } int main() { int a[MAXN] = {0}, la; int b[MAXN] = {0}, lb; int c[MAXN] = {0}, lc; string s1, s2; cin >> s1 >> s2; str_to_arr(s1, a, la); str_to_arr(s2, b, lb); add(a, la, b, lb, c, lc); print_arr(c, lc); return 0; }测试输入:
9999 1输出:
10000
二、高精度减法(A-B,要求 A≥B)
竖式规则:从个位相减,不够减向前一位借1当10
使用前必须先判断大小!如果A<B,结果是负数,零基础简化版只实现大数减小数
#include <iostream> #include <cstring> using namespace std; const int MAXN = 1005; // 比较两个高精度数:a>=b 返回true bool cmp(int a[], int la, int b[], int lb) { // 位数长的数字更大 if(la != lb) return la > lb; // 位数相同,从最高位依次比较 for(int i = la - 1; i >= 0; i--) { if(a[i] != b[i]) return a[i] > b[i]; } return true; // 两数相等 } // 高精度减法 a-b,前提 a >= b void sub(int a[], int la, int b[], int lb, int c[], int &lc) { lc = la; // 结果最多和被减数位数一样 int borrow = 0; // borrow 借位 for(int i = 0; i < lc; i++) { // 当前位减去之前的借位 int now = a[i] - borrow - b[i]; borrow = 0; if(now < 0) { now += 10; borrow = 1; // 需要向高位借1 } c[i] = now; } // 清除前导0(高位多余的0) while(lc > 1 && c[lc - 1] == 0) lc--; } void str_to_arr(string s, int num[], int &len) { len = s.size(); for(int i = 0; i < len; i++) num[len - 1 - i] = s[i] - '0'; } void print_arr(int num[], int len) { for(int i = len - 1; i >= 0; i--) cout << num[i]; cout << endl; } int main() { int a[MAXN]={0}, la; int b[MAXN]={0}, lb; int c[MAXN]={0}, lc; string s1,s2; cin>>s1>>s2; str_to_arr(s1,a,la); str_to_arr(s2,b,lb); if(cmp(a,la,b,lb)) { sub(a,la,b,lb,c,lc); print_arr(c,lc); } else { cout<<"-"; sub(b,lb,a,la,c,lc); print_arr(c,lc); } return 0; }测试输入:
1000 999输出:
1测试:
123 456→ 输出-333
三、高精度乘法(A × B)
竖式原理:a第i位 × b第j位 → 结果落在
i+j位置#include <iostream> #include <cstring> using namespace std; const int MAXN = 1005; void mul(int a[], int la, int b[], int lb, int c[], int &lc) { memset(c, 0, sizeof(c)); // 双重循环模拟竖式每一位相乘 for(int i = 0; i < la; i++) { int carry = 0; for(int j = 0; j < lb; j++) { // 原有数字 + a[i]*b[j] + 进位 long long sum = c[i+j] + 1LL * a[i] * b[j] + carry; c[i+j] = sum % 10; carry = sum / 10; } // 剩余进位继续向后放 if(carry != 0) c[i + lb] += carry; } // 最大位数 la+lb lc = la + lb; // 去除前导零 while(lc > 1 && c[lc - 1] == 0) lc--; } void str_to_arr(string s, int num[], int &len) { len = s.size(); for(int i = 0; i < len; i++) num[len - 1 - i] = s[i] - '0'; } void print_arr(int num[], int len) { for(int i = len - 1; i >= 0; i--) cout << num[i]; cout << endl; } int main() { int a[MAXN]={0}, la; int b[MAXN]={0}, lb; int c[MAXN]={0}, lc; string s1,s2; cin>>s1>>s2; str_to_arr(s1,a,la); str_to_arr(s2,b,lb); mul(a,la,b,lb,c,lc); print_arr(c,lc); return 0; }输入:
9999 9999输出:99980001注意:
1LL * a[i] * b[j]防止乘法溢出!
四、高精度除法(大数 ÷ 普通整数,零基础首选)
⚠️ 高精度÷高精度逻辑极其复杂,初学先掌握:高精度大数 ÷ int 整数(竞赛最常用) 思路:从最高位往低位依次试商
#include <iostream> #include <cstring> using namespace std; const int MAXN = 1005; // a被除数,la长度;b除数(普通int);c商;rem余数 void div(int a[], int la, int b, int c[], int &lc, long long &rem) { rem = 0; lc = 0; // 除法正向遍历!从最高位开始(和加减乘反过来) for(int i = la - 1; i >= 0; i--) { rem = rem * 10 + a[i]; c[lc++] = rem / b; // 当前位商 rem = rem % b; // 更新余数 } // c数组现在正向存储商,需要翻转!同时清除前导0 int temp[MAXN] = {0}, tl = 0; bool flag = false; for(int i = 0; i < lc; i++) { if(c[i] == 0 && !flag) continue; // 跳过前导0 flag = true; temp[tl++] = c[i]; } // 特判结果为0 if(tl == 0) tl = 1; // 逆序放回c,统一和加减乘格式保持一致 memset(c, 0, sizeof(c)); lc = tl; for(int i = 0; i < lc; i++) c[lc - 1 - i] = temp[i]; } void str_to_arr(string s, int num[], int &len) { len = s.size(); for(int i = 0; i < len; i++) num[len - 1 - i] = s[i] - '0'; } void print_arr(int num[], int len) { for(int i = len - 1; i >= 0; i--) cout << num[i]; cout << endl; } int main() { int a[MAXN]={0}, la; int c[MAXN]={0}, lc; long long rem; string s; int b; cin >> s >> b; str_to_arr(s,a,la); div(a, la, b, c, lc, rem); cout << "商:"; print_arr(c, lc); cout << "余数:" << rem << endl; return 0; }输入:
1234567890123456789 7输出商和余数零基础学习路线建议
- 先看懂【逆序数组存储】核心思想
- 吃透高精度加法(进位逻辑)
- 学习减法(借位 + 大小比较)
- 学习乘法(双重循环)
- 学习高精度除普通整数
- 有余力再拓展【高精度 ÷ 高精度】
通用函数总结(四个代码共用)
str_to_arr:字符串 → 逆序数字数组(输入)print_arr:逆序数组 → 正常数字输出
常见踩坑提醒
- 数组一定要初始化0!
int a[MAXN]={0} - 乘法容易溢出,相乘前强制转long long
- 减法、乘法记得删除前导零(比如 00123 → 123)
- 加法最后别忘记处理最后的进位
- 数字逆序存在数组(低位在下,数组下标0存个位)
例:数字
-
@ 2026-7-31 20:16:46
C++ 高精度四则运算【0基础易懂教程】
先搞懂核心概念
普通变量局限:
int、long long能存的数字有限,如果数字长达几十位、上百位,存不下! 解决方案:用数组/vector保存每一位数字,手动模拟竖式笔算统一存储规则(四则全部通用,务必记住)
✅ 低位存在数组前面(下标小的位置) 例:数字
123= 百位1,十位2,个位3 存储方案:a[1]=3(个位),a[2]=2(十位),a[3]=1(百位)a[0]专门存这个数字一共有多少位 →a[0]=3为什么倒着存? 计算产生进位时,直接往后面下标
i+1写数字,不用整体挪动数组,写代码最简单!输入字符串
123→ 逆序放进数组 输出数组 → 从后往前打印
工具函数(四个运算共用,先复制)
#include <iostream> #include <vector> #include <string> using namespace std; // 函数1:字符串 → 高精度数组(低位在前) vector<int> str2big(string s) { vector<int> num; num.push_back(s.size()); // num[0] 存储位数 // 字符串末尾是个位,倒着存入数组 for(int i = s.size() - 1; i >= 0; i--) { num.push_back(s[i] - '0'); } return num; } // 函数2:打印高精度数字 void print(vector<int> num) { // 从高位往低位输出(数组尾部是高位) for(int i = num[0]; i >= 1; i--) { cout << num[i]; } cout << endl; } // 函数3:比较两个高精度数大小 a >= b 返回true(减法、除法要用) bool compare(vector<int> a, vector<int> b) { // 位数长的数字更大 if(a[0] != b[0]) return a[0] > b[0]; // 位数相同,从高位逐一对比 for(int i = a[0]; i >= 1; i--) { if(a[i] != b[i]) return a[i] > b[i]; } return true; // 两个数相等 }1. 高精度加法(a + b)
笔算思路:个位开始相加,满10进1
// 高精度加法 a + b vector<int> add(vector<int> a, vector<int> b) { vector<int> res; int carry = 0; // carry 进位,初始0 // 只要还有位数没算完 或者 还有进位,继续循环 for(int i = 1; i <= a[0] || i <= b[0] || carry; i++) { // 当前位累加,没有数字就默认加0 if(i <= a[0]) carry += a[i]; if(i <= b[0]) carry += b[i]; res.push_back(carry % 10); // 当前位保留个位 carry = carry / 10; // 算出新的进位 } res[0] = res.size(); // 记录总位数 return res; } // 加法测试main /* int main() { string s1, s2; cin >> s1 >> s2; vector<int> a = str2big(s1); vector<int> b = str2big(s2); vector<int> ans = add(a,b); print(ans); return 0; } 测试输入:123456 7890 输出:131346 */2. 高精度减法(a - b,支持负数结果)
笔算思路:逐位相减,不够向前一位借1当10
// 高精度减法 a - b 前提 a >= b vector<int> sub(vector<int> a, vector<int> b) { vector<int> res; int borrow = 0; // borrow 借位标记 for(int i = 1; i <= a[0]; i++) { int cur = a[i] - borrow; // 先减去之前的借位 if(i <= b[0]) cur -= b[i]; if(cur < 0) // 不够减,需要借位 { cur += 10; borrow = 1; // 标记需要向前借位 } else { borrow = 0; } res.push_back(cur); } // 删除高位多余前导0!例如1000-999得到0001 → 删掉前面0 while(res.size() > 1 && res.back() == 0) res.pop_back(); res[0] = res.size(); return res; } // 减法完整测试main(自动判断正负) /* int main() { string s1, s2; cin >> s1 >> s2; vector<int> a = str2big(s1); vector<int> b = str2big(s2); vector<int> ans; if(compare(a,b)) { ans = sub(a,b); } else { cout << "-"; // 结果负数 ans = sub(b,a); } print(ans); return 0; } 测试1:1000 999 → 1 测试2:999 1000 → -1 */3. 高精度乘法
分两种: ① 大数 × 普通整数(竞赛高频,简单) ② 大数 × 大数(拓展)
3.1 高精度 × 低精度(推荐初学先掌握)
// 高精度数字 a * 普通整数x vector<int> mul_small(vector<int> a, int x) { vector<int> res; long long carry = 0; // 使用long long防止相乘溢出 for(int i = 1; i <= a[0] || carry; i++) { if(i <= a[0]) carry += (long long)a[i] * x; res.push_back(carry % 10); carry /= 10; } // 清除前导0 while(res.size() > 1 && res.back() == 0) res.pop_back(); res[0] = res.size(); return res; } /* main测试 int main() { string s; int x; cin >> s >> x; vector<int> a = str2big(s); vector<int> ans = mul_small(a,x); print(ans); } 输入:123456789 100 输出:12345678900 */3.2 高精度 × 高精度(大数×大数)
vector<int> mul_big(vector<int> a, vector<int> b) { vector<int> res(a[0] + b[0], 0); // 预先分配最大长度 // 双重循环模拟竖式 for(int i = 1; i <= a[0]; i++) { long long carry = 0; for(int j = 1; j <= b[0] || carry; j++) { if(j <= b[0]) carry += (long long)a[i] * b[j]; carry += res[i + j - 1]; res[i + j - 1] = carry % 10; carry /= 10; } } // 删掉前导零 while(res.size() > 1 && res.back() == 0) res.pop_back(); res[0] = res.size() - 1; return res; } /* 测试main int main() { string s1,s2; cin>>s1>>s2; auto a=str2big(s1),b=str2big(s2); auto ans=mul_big(a,b); print(ans); } 输入:999 999 输出998001 */4. 高精度 ÷ 低精度(初学优先掌握,返回商和余数)
高精度÷高精度难度很高,新手先学【大数 ÷ 普通整数】 ⚠️ 除法和前面反过来!从高位开始计算
// a / x 商存在quot,余数作为返回值 long long div_small(vector<int> a, int x, vector<int>& quot) { quot.clear(); long long rem = 0; // rem 余数 // 除法从高位往低位算!倒着遍历 for(int i = a[0]; i >= 1; i--) { rem = rem * 10 + a[i]; quot.push_back(rem / x); rem = rem % x; } // 当前quot是高位在前,反转成【低位在前】统一格式 reverse(quot.begin(), quot.end()); // 清除前导0 while(quot.size() > 0 && quot.back() == 0) quot.pop_back(); // 特判商为0的情况 if(quot.empty()) quot.push_back(0); quot.push_back(quot.size()); swap(quot[0], quot.back()); // 把长度放到下标0 return rem; } /* main测试 int main() { string s; int x; cin >> s >> x; vector<int> a = str2big(s); vector<int> shang; long long yu = div_small(a, x, shang); cout << "商:"; print(shang); cout << "余数:" << yu << endl; } 输入:12345 7 商:1763 余数4 */完整整合说明
- 所有代码复制到同一个cpp文件即可运行
- 想测试哪个运算,就打开对应注释里的main函数,删掉其他main
- 学习顺序建议:加法 → 减法 → 大数乘小数 → 大数除小数 → 大数乘大数
新手常见坑提醒
- ❌ 忘记清除前导0,输出一堆000123
- ❌ 乘法不用long long存进位,发生数据溢出
- ❌ 混淆高低位,加法减法低位在前,除法遍历顺序相反
- ❌ 借位、进位忘记初始化,循环出错
- 1