CSP初赛复习-24-高精度运算

CSP初赛复习-24-高精度运算

数据类型

在 C 语言中,数据类型指的是用于声明不同类型的变量,变量的类型决定了变量存储占用的空间,不同类型占用存储空间不同并且所表示的数据范围不同

常用类型范围

类型 存储大小 值范围
char 1 字节 -128 到 127
unsigned char 1 字节 0 到 255
short 2 字节 -32,768 到 32,767
unsigned short 2 字节 0 到 65,535
int 4 字节 -2,147,483,648 到 2,147,483,647
unsigned int 4 字节 0 到 65,535 或 0 到 4,294,967,295
long long 8 字节 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
unsigned long long 8 字节 0 到 18,446,744,073,709,551,615

数据溢出

在C语言中,数据溢出是指变量在进行计算或赋值操作时超出了其数据类型所能表示的范围,导致结果不正确或不可预料的行为

例如

#include<bits/stdc++.h>
using namespace std;
int main(){
	char c=129;//超出了char 表示的数值范围 char最大是127
	printf("%d",c); 
	return 0;
} 
/*
输出
-127 
*/

高精度计算

在编程进行数值运算时,有时会遇到运算的表示范围的数值,这种情况下,就需要进行高精度运算。

高精度运算一般包括:高精度加法、高精度减法、高精度乘法、高精度除法。

高精度加法

P1601 A+B Problem

https://www.luogu.com.cn/problem/P1601

大致思路

1 通过数字模拟数字翻转按位相加

2 累加后数字大于9(有2位产生时),需要借助进位变量记录进位数

3 进位数参与下一位累加计算

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 506;
int a[MAXN], b[MAXN], sum[MAXN];
char ac[MAXN], bc[MAXN];
//int lena, lenb, lensum;
// 使用a[0] b[0] sum[0] 表示长度 
void convert(char ca[], int a[]){//char数组转int数组 且倒序 方便按位加法运算
    int len = strlen(ca);
    for(int i = 1; i <= len; i++){
        a[i] = ca[len - i] - '0';
    }
    a[0] = len;//a数组第0个元素存储数组存放数字长度
}
void print(int ans[]){//从后向前逐一输出
    for(int i = ans[0]; i >= 1; i--)
        printf("%d", ans[i]);
}

int add(int a[], int b[], int sum[]){
    int i = 1, x = 0;// i表示当前相加的2数的位 x表示进位
    while(i <= a[0] || i <= b[0]) {//逐位相加 加到位数多的最后一位
        sum[i] = a[i] + b[i] + x;//当前位2个数字和进位一起加
        x = sum[i]/10;//计算进位赋值给x
        sum[i] = sum[i] % 10;//只保留个位 
        i++;//累加下一位
    }
    while(x) {//如果最后一位x不是0 前面有进位需要处理
        sum[i] = x % 10;//把进位赋值给下一位
        x /= 10;//去除x
        i++;//累加下一位
    }
    sum[0] = i - 1;//所有位数为i-1  因为当期位累加后进行来一次i++
}

int main(){
    scanf("%s", ac);//输入一个数
    scanf("%s", bc);//输入另一个数
    convert(ac, a);//字符数组转int数组
    convert(bc, b);//字符数组转int数组
    add(a, b, sum);//累加
    print(sum);//输出
    return 0;
}

高精度减法

P2142 高精度减法

https://www.luogu.com.cn/problem/P2142

大致思路

1 数据处理 保证大数-小数,如果不是需要加负号

2 按位相减 减后如果当前为数字小于0,需要向高位借1当10

3 结果位数计算 默认和大数位数相同,如果后面都是0 则不是结果的位数,需要找到第1个不是0的数字

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 10088;
char ac[MAXN], bc[MAXN];
int a[MAXN], b[MAXN], ans[MAXN];
// 使用a[0] b[0] ans[0] 表示长度 
void convert(char ca[], int a[]){//char数组转int数组 且倒序 方便按位运算
    int len = strlen(ca);
    for(int i = 1; i <= len; i++){
        a[i] = ca[len - i] - '0';
    }
    a[0] = len;//a数组第0个元素存储数组存放数字长度
}
void print(int ans[]){//从后向前逐一输出
    for(int i = ans[0]; i >=1; i--)
        printf("%d", ans[i]);
}
int compare(int a[], int b[]){//比较2数组大小 a>b 返回1 其他返回0
    int lena = a[0];//lena a数组长度
    int lenb = b[0];//lenb b数组长度
    if(lena > lenb) return 1;//长度长的数大  a数组大
    if(lenb > lena) return -1;//长度长的数大  b数组大
    for(int i = lena; i >=1; i--){//位数相同 逐一比较
        if(a[i] > b[i]) return 1;//位在后面的数字大(后面权重大) 则大
        if(a[i] < b[i]) return -1;//位在后面的数字小(后面权重大) 则小
    }
    return 0; //a==b 返回0
}
// 参与减法运算的a和b数组 ans减法计算结果
int sub(int a[], int b[], int ans[]){
    int i;
    int *big, *little;//通过2个指针变量 可以变换数组a和数字b
	//需要保证大数-小数
    if(compare(a, b) >= 0) {//big指向大的数组 little指向小的数组
        big = a;
        little = b;    
    } else {
        big = b;
        little = a;
    }
    //从个位开始按位减 直到被减数最高位
    for(i = 1; i <= big[0]; i++) {
        ans[i] = big[i] - little[i] + ans[i];//按位减 ans[i] 正常是0,如果被减位为-1
        if(ans[i] < 0) {//如果计算结果为<0 需要向高位借1当10
            ans[i] += 10;//当10
            ans[i + 1] -= 1;//高位借1
        }
    }
    i = big[0];//i 计算结果位数 最大和big位数相同
    while(ans[i] == 0 && i > 1)//高位是0说明结果没到这一位 逐渐减少i
        i--;
    if(big == b)//前面经过调换保证使用大数-小数 如果b是大数 需要在最后加负号
        ans[i] *= -1;
    ans[0] = i;//结果位数赋值给ans[0]
}
int main(){
    scanf("%s", ac);//输入一个数
    scanf("%s", bc);//输入另一个数
    convert(ac, a);//字符数组转int数组
    convert(bc, b);//字符数组转int数组
    sub(a, b, ans);//a b 2数相减 结果保存在ans 
    print(ans);//输出    
    return 0;
}

高精度乘法

P1303 A*B Problem

https://www.luogu.com.cn/problem/P1303

大致思路

1 从个位开始,逐位相乘 ,本次相乘下标分别i,j 则 相乘结果保存下标i+j-1,可通过模拟2位相乘得出

2 逐位相乘时先不进位,此时结果数组中可能有2位数存在

3 相乘结束后,再逐位进位

4 2数相乘后的位数,最大是2数位数之和

5 从可能的最大位开始,计算结果的位数,如果高位是0,则此位不是结果的一个位,则进行减少位数

#include <bits/stdc++.h>
using namespace std;
char ac[2008], bc[2008];
int a[2008], b[2008], ans[4000008];
// 使用a[0] b[0] ans[0] 表示长度 
void convert(char ca[], int a[]){//char数组转int数组 且倒序 方便按位运算
    int len = strlen(ca);
    for(int i = 1; i <= len; i++){
        a[i] = ca[len - i] - '0';
    }
    a[0] = len;//a数组第0个元素存储数组存放数字长度
}
void print(int ans[]){//从后向前逐一输出
    for(int i = ans[0]; i >=1; i--)
        printf("%d", ans[i]);
}
// a b 数组为参与相乘的2个数  ans数组为相乘的结果
void mult(int a[], int b[], int ans[]){
    int i, j, len;//i j 分别代表2乘数当前参与相乘的位数
    for(i = 1; i <= a[0]; i++){
        for(j = 1; j <= b[0]; j++) {
            //相乘结果下标为i+j-1 用2位数简单模拟归纳
            ans[i+j-1] += a[i] * b[j];//相乘结果先不进位,后面统一进位
        }
    }
    len = a[0] + b[0];//2数相乘位数最大为2数位数之和
    for(i = 1; i < len; i++) {//循环进位
        ans[i + 1] += ans[i] / 10;//进位
        ans[i] %= 10;//当前只保留最后一位
    }
    while(ans[i] == 0 && i > 1)//计算结果的位数 - 从最高位开始,为0的位去除
        i--;
    ans[0] = i;//结果位数赋值给ans[0]
}
int main(){
    scanf("%s", ac);//输入一个数
    scanf("%s", bc);//输入另一个数
    convert(ac, a);//字符数组转int数组
    convert(bc, b);//字符数组转int数组
    mult(a, b, ans);//a b 2数相乘 结果保存在ans 
    print(ans);//输出 
    return 0;
}

高精度除法-高精度除以单精度

P1480 A/B Problem

https://www.luogu.com.cn/problem/P1480

大致思路

1 从高位开始, 需要注意加上高位余数

2 当前位商保存ans 余数保留参与下一次除法运算

3 结果位数最大为被除数的位数 ,同时去除高位前导0

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5008;
char ac[MAXN];
int a[MAXN], b, ans[MAXN];
// 使用a[0] b[0] ans[0] 表示长度 
void convert(char ca[], int a[]){//char数组转int数组 且倒序 方便按位运算
    int len = strlen(ca);
    for(int i = 1; i <= len; i++){
        a[i] = ca[len - i] - '0';
    }
    a[0] = len;//a数组第0个元素存储数组存放数字长度
}

void print(int ans[]){//从后向前逐一输出
    for(int i = ans[0]; i >=1; i--)
        printf("%d", ans[i]);
}
/*
  高精度a数组 除以 整数b 结果保存数组ans
*/
void div(int a[], int b, int ans[]){
    int i, x = 0;// i 被除的位 0 当前位的余数 
    for(i = a[0]; i >= 1; i--) {//从高位开始除
        x = x * 10 + a[i];//高位如果有余数 需要把余数加进来参与当前为除法运算
        ans[i] = x / b;//当前位的商
        x = x % b;//当前位的余数 参与下一位计算
    }
    i = a[0];//结果位数最大为被除数的位数
    while(ans[i] == 0 && i > 1)//高位为0舍去
        i--;
    ans[0] = i;//位数赋值ans[0]
}
int main(){
    scanf("%s", ac);//输入一个数
    scanf("%d", &b);//输入另一个数
    convert(ac, a);//字符数组转int数组
    div(a, b, ans);//高精度a数组 除以单精度b 结果保存到ans数组
    print(ans);//输出  
    return 0;
}

高精度除以单精度余数

大致思路

1 从高位开始, 需要注意加上高位余数

2 每次和除数b取余数 ,最后保留下来的就是要求的余数

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5008;
char ac[MAXN];
int a[MAXN], b;
// 使用a[0] b[0] ans[0] 表示长度 
void convert(char ca[], int a[]){//char数组转int数组 且倒序 方便按位运算
    int len = strlen(ca);
    for(int i = 1; i <= len; i++){
        a[i] = ca[len - i] - '0';
    }
    a[0] = len;//a数组第0个元素存储数组存放数字长度
}

/*
  高精度a数组 和单精度整数b 取余数 
*/
int rem(int a[], int b){
    int i, x = 0;// i 被除的位 0 当前位的余数 
    for(i = a[0]; i >= 1; i--) {//从高位开始除
        x = x * 10 + a[i];//高位如果有余数 需要把余数加进来参与当前为除法运算
        x = x % b;//当前位的余数 参与下一位计算
    }
    return x;//x就是对应余数 
}
int main(){
    scanf("%s", ac);//输入一个数
    scanf("%d", &b);//输入另一个数
    convert(ac, a);//字符数组转int数组
    int r=rem(a, b);//高精度a数组 和单精度b 取余数 
    cout<<r; 
    return 0;
}
posted @ 2023-08-07 21:43  new-code  阅读(104)  评论(0)    收藏  举报