大整数:高精度整数,其含义是基本数据类型无法存储其精度的整数。
整数的高位存储在数组的高位,整数的低位存储在数组的低位。不反过来的原因是,在运算的时候都是从整数的低位到高位进行枚举,顺位存储和这种思维相合。
例如:235813 存储在数组中,即有d[0] = 3,d[1]= 1,d[2] = 8, d[3] = 5, d[4] =3, d[5] = 2;
注意: 把整数按字符串%s读入的时候,实际上是逆位存储的,即str[0] =‘2’, str[3] = ‘3’,str[5]= ‘3’,因此在读入之后,需要另存为至d[]数组的时候反转一下。
为了方便获取大整数的长度,一般会定义一个int类变量len来记录其长度,并和d数组合成结构体:
struct bign{
int d[1000];
int len;
bign(){
memset(d,0, sizeof(d));
len = 0;
}
};
初始化结构体,是为了减少在输入代码时,总是忘记初始化的问题。
struct bign
{
int d[1000];
int len;
bign(){//构造函数(函数名与构造体相同,无返回值)
memset(d, 0, sizeof(d));
len = 0;
}
};
在输入大整数的时,一般都是先用字符串读入,然后再把字符串另存至bign结构体。
bign change(char str[]){//将整数转换为bign
bign a;
a.len = strlen(str);//bign的长度就是字符串的长度
for (int i = 0; i < a.len; ++i)
{
a.d[i] = str[a.len - i - 1] - '0';//逆序赋值
}
return a;
}
比较两个bign变量的大小:
先判断两者的len大小,如果不想等,则以长的为大,如果相等,则从高位到低位进行比较,直到出现某一位不相等,就可以判断两个数的大小。
int campare(bign a, bign b){
if(a.len > b.len){//a大
return 1;
}else if(a.len < b.len){//a小
return -1;
}else{//从高位往地位比较
for (int i = a.len - 1; i >= 0; --i)
{
if(a.d[i] > b.d[i]){//只要有一位a大,则a大
return 1;
}else if(a.d[i] < b.d[i]){//只要有一位a小,则a小
return -1;
}
}
return 0;
}
}
根据整数的加法,可以得知:
对其中一位进行加法的步骤:将该位上的数组和进位相加,得到的结果取个位作为该位结果,取十位数作为新的进位。
注意:这样的写法条件:是两个对象都是非负数,如果有一方时负数,可以在转换到数组这一步时去掉符号,然后采用高精度减法;
如果两者都是负的,就都去掉负号后用高精度加法,最后再把负数加回去即可。
bign add(bign a, bign b){
bign c;
int carry = 0;//进位
for (int i = 0; i < a.len || i < b.len; ++i)//以较长的为界
{
int temp = a.d[i] + b.d[i] + carry;//两个对应位与进位相加
carry = temp / 10;//十位为新的进位
c.d[c.len++] = temp % 10;//各位为该位结果
}
if(carry != 0){//如果最后进位不为0,则直接赋值给结果的最高位
c.d[c.len++] = carry;
}
return c;
}
#include
#include
struct bign
{
int d[1000];
int len;
bign(){//构造函数
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(char str[]){//将整数转换为bign
bign a;
a.len = strlen(str);//bign的长度就是字符串的长度
for (int i = 0; i < a.len; ++i)
{
a.d[i] = str[a.len - i - 1] - '0';//逆序赋值
}
return a;
}
int campare(bign a, bign b){
if(a.len > b.len){//a大
return 1;
}else if(a.len < b.len){//a小
return -1;
}else{//从高位往地位比较
for (int i = a.len - 1; i >= 0; --i)
{
if(a.d[i] > b.d[i]){//只要有一位a大,则a大
return 1;
}else if(a.d[i] < b.d[i]){//只要有一位a小,则a小
return -1;
}
}
return 0;
}
}
bign add(bign a, bign b){
bign c;
int carry = 0;//进位
for (int i = 0; i < a.len || i < b.len; ++i)//以较长的为界
{
int temp = a.d[i] + b.d[i] + carry;//两个对应位与进位相加
carry = temp / 10;//十位为新的进位
c.d[c.len++] = temp % 10;//各位为该位结果
}
if(carry != 0){//如果最后进位不为0,则直接赋值给结果的最高位
c.d[c.len++] = carry;
}
return c;
}
void print(bign a){
for (int i = a.len - 1; i >= 0; --i)//输出bign
{
printf("%d", a.d[i]);
}
}
int main(int argc, char const *argv[])
{
char str[1000],str2[1000];
scanf("%s%s",str, str2);
bign a = change(str);
bign b = change(str2);
print(add(a,b));
return 0;
}
对于某一步,比较被减位和减位,如果不够减,则令被减位的高位减1、被减位加10再进行减法;如果够减,则直接减。
最后一步要注意减法后高位可能有多余的0,要忽略它们,但也要保证结果至少有一位。
bign sub(bign a, bign b){
bign c;
for (int i = 0; i < a.len || i < b.len; ++i)//以较长为边界
{
if(a.d[i] < b.d[i]){//如果不够借位
a.d[i + 1]--;//向高位借位
a.d[i]+=10;//当前为加10
}
c.d[c.len++]= a.d[i] - b.d[i];//减法结果为当前位结果
}
while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){
c.len--;//去除高位的0,至少还保留一位最低位
}
return c;
}
低精度就是可以用基本数据类型存储的数据,例如int型。
以 147 × 35 147\times35 147×35为例,这里147视为高精度bign类型,而35视为int类型。并在下面的过程中,始终将35看成一个整体。
147 × 35 2 4 5 140 35 5 1 4 5 \quad147\\ \frac{\times \quad35}{2\,4\,5}\\ 140\quad\\ \frac{35\quad\;\;}{5\,1\,4\,5} 147245×35140514535
步骤:
bign multi(bign a, int b){
bign c;
int carry = 0;//进位
for (int i = 0; i < a.len; ++i)
{
int temp = a.d[i]* b + carry;
c.d[c.len++] = temp % 10;//个位作为该位结果
carry = temp /10;//高位部分作为新的进位
}
while(carry != 0){//和加法不一样,进位可能不止一位
c.d[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
除法的计算方法与小学的是相同的。
以1234/7为例:
0176 2 1234 7 53 49 44 42 2 \quad\;0176\\\\ ^{2}\sqrt{1234}\\ \frac{7}{ \quad \;53 \quad}\\ \frac{\;49}{ \;\quad \quad44 \quad}\\ \frac{\;\quad42}{ \;\;\quad \quad\;2 \quad}\\ 0176212345374449242
步骤:
归纳其中某一步的步骤:上一步的余数乘以10加上该步的位,得到该步临时的被除数,将其与除数比较:如果不够除,则该位的商为0;如果够除,则商即为对应的商,余数即为对应的余数。最后一步要注意减法后高位可能有多余的0,要忽略它,但也要保证结果至少有一位。
bign divide(bign a, int b, int &r){//r为余数
bign c;
c.len = a.len;//被除数的每一位与商的每一位是一一对应的,因此先令长度相等
for (int i = a.len -1; i >= 0; --i)
{
r = r * 10 + a.d[i];//与上一位遗留的余数组合
if(r < b){//不够除
c.d[i] = 0;
}else{//够除
c.d[i] = r/b; //商
r = r %b;//获得新余数
}
}
while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){
c.len--;//去除高位0,同时至少保留一位最低位
}
return c;
}
高精度的除法(a小于b,带小数):
int cmp(string a, string b){
unsigned long i1 = a.find_first_not_of('0');
unsigned long i2 = b.find_first_not_of('0');
if(i1 == string::npos) a = '0';
else a.substr(i1);
if(i2 == string::npos) b = '0';
else b.substr(i2);
if(a.length() > b.length()) return 1;
else if(a.length() < b.length()) return -1;
else{ //长度相等
if(a < b) return -1;
if(a > b) return 1;
else return 0;
}
}
//此处a一定大于等于b
string subtract(string a, string b){
//完整减法里,a可以小于b,这结果为负数,交换ab进行下面的代码
//反转
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
//按位做减法
for (int i = 0; i < b.length(); ++i)
{
if(a[i] >= b[i]){
a[i] = a[i] - b[i] + '0';
}else{//小了就要借位
int k = 1;
while(a[i + k] == '0') {//这里可以保证i+k这一位不是0
a[i + k] = '9';
k++;
}
a[i + k] = a[i + k] - '1' + '0';
a[i] = (a[i] - '0' + 10) - (b[i] - '0') + '0';
}
}
reverse(a.begin(), a.end());
if(a.find_first_not_of('0') == string::npos) return "0";
return a.substr(a.find_first_not_of('0'));
}
//除法作为减法
string divide(string a, string b){
//只考虑 a < b 的情况
string ans = "0.";
//转化为减法
for (int i = 0; i < 101; ++i)
{
a.append("0");
int t = 0;
while(cmp(a,b) >= 0){ //a > b
a = subtract(a, b); //不停地做减法
t++;//记录减法做了多少次
}
string t_str;
//i2s(t ,t_str); //is2()是windows特有的库函数
stringstream stream;
stream << t;
stream >> t_str;
ans.append(t_str);
}
return ans;
}
#include
#include
#include
using std::string;
int main(){
char s[40],ss[40];
int a[40],b[40],c[80];
memset(a,0,sizeof(a)); //清零数组
memset(b,0,sizeof(b));
memset(c,0,sizeof(c)); //清零
scanf("%s%s",s, ss);
int len = strlen(s);
int lenn = strlen(ss);
for (int i = 0 ; i < len ; i++) a[len - i - 1] = s[i] - '0';//将字符串转化为数组
for (int i = 0 ; i < lenn ; i++) b[lenn - i - 1] = ss[i] - '0';
for (int i = 0 ; i < len ; i++)
for (int j = 0 ; j < lenn ; j++)
c[i + j] += a[i] * b[j]; //运算(这个就有一点复杂了)
int l = len + lenn - 1; //l是结果的最高位数
for (int i = 0 ; i < l ;i++)
{
c[i + 1] += c[i] / 10; //保证每一位的数都只有一位,并进位
c[i] %= 10;
}
if (c[l] > 0) l++; //保证最高位数是对的
while (c[l - 1] >= 10)
{
c[l] = c[l - 1] / 10;
c[l - 1] %= 10;
l++;
}
while (c[l - 1] == 0 && l > 1) l--; //while去零法
for (int i = l - 1; i >= 0 ; i--) //输出结果
printf("%d",c[i]);
return 0;
}
#include
#include
#include
#include
using namespace std;
void i2s(int i, string& s){
stringstream stream;
stream << i;
stream >> s;
}
string add(string a, string b){
a = a.substr(a.find_first_not_of('0'));//去掉开头的零
b = b.substr(b.find_first_not_of('0'));
long long lenA = a.length();
long long lenB = b.length();
long long len = max(lenA,lenB) + 1;//有可能A、B长度相同,相加后进一位
reverse(a.begin(), a.end()); //反转便于低位逐步求和
reverse(b.begin(), b.end());
string ans(len,'0'); //初始化答案为len长,全部为字符0
for (int i = 0; i < lenA; ++i)//把a拷贝到ans中
{
ans[i] = a[i];
}
int temp = 0; //temp是上一位相加后的进位
for (int i = 0; i < len; ++i)
{
if(i < b.length()){
temp += (ans[i] - '0') + (b[i] - '0');
}else{
temp += (ans[i] - '0');
}
ans[i] = temp % 10 + '0';
temp /= 10;
}
reverse(ans.begin(), ans.end());
return ans.substr(ans.find_first_not_of('0'));
}
int cmp(string a, string b){
unsigned long i1 = a.find_first_not_of('0');
unsigned long i2 = b.find_first_not_of('0');
if(i1 == string::npos) a = '0';
else a.substr(i1);
if(i2 == string::npos) b = '0';
else b.substr(i2);
if(a.length() > b.length()) return 1;
else if(a.length() < b.length()) return -1;
else{ //长度相等
if(a < b) return -1;
if(a > b) return 1;
else return 0;
}
}
//此处a一定大于等于b
string subtract(string a, string b){
//完整减法里,a可以小于b,这结果为负数,交换ab进行下面的代码
//反转
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
//按位做减法
for (int i = 0; i < b.length(); ++i)
{
if(a[i] >= b[i]){
a[i] = a[i] - b[i] + '0';
}else{//小了就要借位
int k = 1;
while(a[i + k] == '0') {//这里可以保证i+k这一位不是0
a[i + k] = '9';
k++;
}
a[i + k] = a[i + k] - '1' + '0';
a[i] = (a[i] - '0' + 10) - (b[i] - '0') + '0';
}
}
reverse(a.begin(), a.end());
if(a.find_first_not_of('0') == string::npos) return "0";
return a.substr(a.find_first_not_of('0'));
}
//除法作为减法
string divide(string a, string b){
//只考虑 a < b 的情况
string ans = "0.";
//转化为减法
for (int i = 0; i < 101; ++i)
{
a.append("0");
int t = 0;
while(cmp(a,b) >= 0){ //a > b
a = subtract(a, b); //不停地做减法
t++;//记录减法做了多少次
}
string t_str;
//i2s(t ,t_str); //is2()是windows特有的库函数
stringstream stream;
stream << t;
stream >> t_str;
ans.append(t_str);
}
return ans;
}
int main(int argc, char const *argv[]){
string s1, s2, ans;
cin >> s1 >> s2;
cout << divide(s1, s2) <<endl;
return 0;
}
#include
int main (){
int n,a,b;
while(~scanf("%d%d%d", &a, &b, &n)){
printf("%d.", a/b);//计算整数部分
while(n--){//计算小数部分
a=(a-a/b*b)*10;//重新计算被除数
printf("%d",a/b);
}
printf("\n");
}
//上面的极简形式
// for(;~scanf("%d%d%d",&a,&b,&n);printf("\n"))
// for(printf("%d.",a/b);n>0;a=(a-a/b*b)*10,printf("%d",a/b),--n);
return 0;
}
#include
#include
struct bign
{
int d[1000];
int len;
bign(){//构造函数
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(char str[]){//将整数转换为bign
bign a;
a.len = strlen(str);//bign的长度就是字符串的长度
for (int i = 0; i < a.len; ++i)
{
a.d[i] = str[a.len - i - 1] - '0';//逆序赋值
}
return a;
}
int campare(bign a, bign b){
if(a.len > b.len){//a大
return 1;
}else if(a.len < b.len){//a小
return -1;
}else{//从高位往地位比较
for (int i = a.len - 1; i >= 0; --i)
{
if(a.d[i] > b.d[i]){//只要有一位a大,则a大
return 1;
}else if(a.d[i] < b.d[i]){//只要有一位a小,则a小
return -1;
}
}
return 0;
}
}
bign add(bign a, bign b){
bign c;
int carry = 0;//进位
for (int i = 0; i < a.len || i < b.len; ++i)//以较长的为界
{
int temp = a.d[i] + b.d[i] + carry;//两个对应位与进位相加
carry = temp / 10;//十位为新的进位
c.d[c.len++] = temp % 10;//各位为该位结果
}
if(carry != 0){//如果最后进位不为0,则直接赋值给结果的最高位
c.d[c.len++] = carry;
}
return c;
}
bign sub(bign a, bign b){
bign c;
for (int i = 0; i < a.len || i < b.len; ++i)//以较长为边界
{
if(a.d[i] < b.d[i]){//如果不够借位
a.d[i + 1]--;//向高位借位
a.d[i]+=10;//当前为加10
}
c.d[c.len++]= a.d[i] - b.d[i];//减法结果为当前位结果
}
while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){
c.len--;//去除高位的0,至少还保留一位最低位
}
return c;
}
bign multi(bign a, int b){
bign c;
int carry = 0;//进位
for (int i = 0; i < a.len; ++i)
{
int temp = a.d[i]* b + carry;
c.d[c.len++] = temp % 10;//个位作为该位结果
carry = temp /10;//高位部分作为新的进位
}
while(carry != 0){//和加法不一样,进位可能不止一位
c.d[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
bign divide(bign a, int b, int &r){//r为余数
bign c;
c.len = a.len;//被除数的每一位与商的每一位是一一对应的,因此先令长度相等
for (int i = a.len -1; i >= 0; --i)
{
r = r * 10 + a.d[i];//与上一位遗留的余数组合
if(r < b){//不够除
c.d[i] = 0;
}else{//够除
c.d[i] = r/b; //商
r = r %b;//获得新余数
}
}
while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){
c.len--;//去除高位0,同时至少保留一位最低位
}
return c;
}
void print(bign a){
for (int i = a.len - 1; i >= 0; --i)//输出bign
{
printf("%d", a.d[i]);
}
}
int main(int argc, char const *argv[])
{
char str[1000],str2[1000];
scanf("%s%s",str, str2);
bign a = change(str);
bign b = change(str2);
print(sub(a,b));
return 0;
}