- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
众所周知,在计算机中,每个数据类型都是有存储上限的,那么当数字特别大时应该怎么办呢?这时高精度就产生了。高精度的主要思想就是模拟手算,然后将结果存储到数组中去,相同的,小数也有精度问题,也可以使用相同的思路 。
这里使用 vector 来进行存储,因为这样不需要去管结果有多少位,直接使用 push_back() 函数就行了,虽然和数组比起来会慢一些,不过差别也仅仅是常数而已 。
输入:定义一个字符串,然后将字符串的每位转数字存储起来就行了 。
string a; vector <int> A;
cin>>a;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
输出:请注意,输入的时候我是反过来的,这样做是为了添加元素比较方便,那么输出的时候也要注意倒着输出 。
for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);
上文中已经提到,在进行高精度运算是模拟手算的,那么接下来就来回忆一下,我们是怎么手动做加法的 。
图为用竖式做加法的示例,我们可以发现主要组成部分有两个加数、结果、还有进位,于是我们的变量就可以呼之欲出了 。
vector <int> A; vector <int> B; vector <int> C; int t;
然后我们再使用循环遍历(从0开始)来进行计算,循环位数较大的那个加数的每一位,然后加到 \(t\) 里面就行了 。
那么 \(C_i\) 就等于 \(A_i+B_i\) 再 \(mod~10\) ,进位 \(t\) 就等于 \((A_i+B_i)/10\) 。
注意在循环完之后,如果 \(t\neq 0\) 的话,还要加上一位 。
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
vector<int> add(vector<int>& A,vector<int>& B)
{
vector<int> C;
int t=0;
for(int i=0;i<max(A.size(),B.size());i++)
{
if(i<A.size()) t+=A[i];
if(i<B.size()) t+=B[i];
C.push_back(t%10);
t/=10;
}
if(t) C.push_back(t);
return C;
}
int main()
{
string a,b;
cin>>a;
cin>>b;
vector<int> A;
vector<int> B;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
vector<int> C=add(A,B);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
return 0;
}
首先,先回忆一下我们是怎么用竖式进行减法的 。
与加法不同,我们在列减法的竖式时会把大数放在上面,小数放在下面,因为减法涉及到借位的问题 。
所以说在计算机计算的时候,我们要先判断 \(A\) 是否 \(\geq B\) ,如果 \(<B\) ,为了避免增加代码量我们直接计算 \(-(A-B)\) 就行了 。
那么因为数字特别大,所以我们也需要手写一个比较函数,那么我们是怎么比较两个数的呢?
先比较哪个位数大,位数多的大,如果位数一样,那么分别比较每一位 。
bool cmp(vector<int>& A, vector<int>& B)
{
if (A.size() != B.size())return A.size() > B.size();
for (int i = A.size()-1; i>=0; i--)
{
if (A[i] != B[i]) return A[i] > B[i];
}
return true;
}
类似加法,变量分别为被减数,减数,结果,借位 。
使用循环遍历(从0开始)被减数的每一位 。
借位 \(t\) = \(A_i-(B_i)-t\) ,如果说最终结果小于0,就要借一位,将 \(t+10\) 最后再 \(mod~10\) 便是 \(C_i\) 。
如果借位了 \(t=1\) 否则 \(t=0\) 。
注意:为了方便,我们直接不管结果是否小于0,每次都加10,因为如果结果不小于0的话 \(+10\) \(mod~10\) 就抵消了对结果不影响 。
最后还要去除前导0 。
while (C.size() > 1 && C.back() == 0)
{
C.pop_back();
}
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
bool cmp(vector<int>& A,vector<int>& B)
{
if(A.size()!=B.size()) return A.size()>B.size();
for(int i=A.size()-1;i>=0;i--)
{
if(A[i]!=B[i]) return A[i]>B[i];
}
return true;
}
vector<int> sub(vector<int>& A,vector<int>& B)
{
int t=0;
vector<int> C;
for(int i=0;i<A.size();i++)
{
t=A[i]-t;
if(i<B.size()) t-=B[i];
C.push_back((t+10)%10);
if(t<0) t=1;
else t=0;
}
while(C.size()>1&&C.back()==0) C.pop_back();
return C;
}
int main()
{
string a,b;
vector<int> A,B;
cin>>a;
cin>>b;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
if(cmp(A,B))
{
vector<int> C=sub(A,B);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}
else
{
vector<int> C=sub(B,A);
cout<<'-';
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}
return 0;
}
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
bool cmp(vector<int>& A,vector<int>& B)
{
if(A.size()!=B.size()) return A.size()>B.size();
for(int i=A.size()-1;i>=0;i--)
{
if(A[i]!=B[i]) return A[i]>B[i];
}
return true;
}
bool cmp1(vector<int>& A,vector<int>& B)
{
if(A.size()!=B.size()) return A.size()>B.size();
for(int i=A.size()-1;i>=0;i--)
{
if(A[i]!=B[i]) return A[i]>B[i];
}
return false;
}
vector<int> add(vector<int>A,vector<int>B)
{
vector<int> C;
int t=0;
for(int i=0;i<max(A.size(),B.size());i++)
{
if(i<A.size()) t+=A[i];
if(i<B.size()) t+=B[i];
C.push_back(t%10);
t/=10;
}
if(t) C.push_back(t);
return C;
}
vector<int> sub(vector<int>& A,vector<int>& B)
{
int t=0;
vector<int> C;
for(int i=0;i<A.size();i++)
{
t=A[i]-t;
if(i<B.size()) t-=B[i];
C.push_back((t+10)%10);
if(t<0) t=1;
else t=0;
}
while(C.size()>1&&C.back()==0) C.pop_back();
return C;
}
int main()
{
string a,b;
vector<int> A,B;
cin>>a;
cin>>b;
if(a[0]!='-'&&b[0]!='-')
{
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
if(cmp(A,B))
{
vector<int> C=sub(A,B);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}
else
{
vector<int> C=sub(B,A);
cout<<'-';
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}
}
else if(a[0]=='-'&&b[0]!='-')
{
for(int i=a.size()-1;i>=1;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
vector<int> C=add(A,B);
cout<<'-';
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}
else if(a[0]!='-'&&b[0]=='-')
{
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=1;i--) B.push_back(b[i]-'0');
vector<int> C=add(A,B);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}
else if(a[0]=='-'&&b[0]=='-')
{
for(int i=a.size()-1;i>=1;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=1;i--) B.push_back(b[i]-'0');
if(cmp1(A,B))
{
cout<<'-';
vector<int> C=sub(A,B);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}
else
{
vector<int> C=sub(B,A);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}
}
return 0;
}
高精度乘法与前面有所不同,在我们用竖式计算乘法时,都是一位对一位的,而在这里因为 \(b\) 比较小,所以我们直接拿大数的每一位去乘上 \(b\) 就行了,那么我们还是拿一个竖式举例:
先模拟一下这个例子:
\(C_0=(3×12)~mod~10=6\) \(t_1=3×12/10=3\) \(C_1=(2×12+t_1)~mod~10=7\) \(t_2=2×12/10=2\) 。
\(C_2=(1×12+2)~mod~10=4\) \(t_3=1×12/10=1\) \(C_3=t_3=1\) 。
那么最终的结果就是 1476 。
我们用循环遍历 \(A\) 的每一位就行了 。
如果最后 \(t\neq0\) ,那么就添上 \(t~mod~10\) ,注意因为我们是直接乘上 \(b\) 所以进位不一定是个位数,要用一个循环来做 。
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
vector<int> mul(vector<int>& A,int B)
{
int t=0;
vector<int> C;
for(int i=0;i<A.size();i++)
{
t+=A[i]*B;
C.push_back(t%10);
t/=10;
}
while(t)
{
C.push_back(t%10);
t/=10;
}
return C;
}
int main()
{
string a;
int B;
cin>>a;
cin>>B;
vector<int> A;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
vector<int> C=mul(A,B);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}
因为两个数都很大,所以我们按照手算的方式进行计算,首先先举个例子:
我们可以发现 \(A_0×B_0\) 对应 \(C_0\) \(A_1×B_0\) 对应 \(C_1\) \(A_2×B_0\) 对应 \(C_2\) 以此类推…… 。
我们可以得出 \(A_i×B_j\) 对应 \(C_i\) \(_+\) \(_j\) 。
那么进位 \(t\) 就等于 \(C_i\) \(_+\) \(_j\) \(/10\) 。
与大数乘小数一样,最后还要添上 \(t\) ,不过这里是模拟手算,不需要再循环了 。
因为不能直接往后添数,所以我们的答案先初始化长一点,否则无法存储 vector<int> C(A.size() + B.size() + 7, 0),
最后记得去除前导0 。
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
vector<int> mul(vector<int>& A,vector<int>& B)
{
vector<int> C(A.size()+B.size()+7,0);
for(int i=0;i<A.size();i++)
{
int t=0;
for(int j=0;j<B.size();j++)
{
C[i + j] += A[i] * B[j] + t;
t = C[i + j] / 10;
C[i + j] %= 10;
}
if(t) C[i+B.size()]+=t;
}
while(C.size()>1&&C.back()==0) C.pop_back();
return C;
}
int main()
{
string a,b;
cin>>a; cin>>b;
vector<int> A,B;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
vector<int> C=mul(A,B);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}
注意: \(t\) 在一轮用完后要及时初始化为0,另外 \(t\) 一定要赋值为 \(C_i\) \(_+\) \(_j/10\) ,因为当重叠时可能加法上又有进位.
首先,回想一下我们是怎么用竖式算除法的 。
我们可以发现几个变量分别是被除数、除数、商、余数 。
我们使用一个变量 \(r\) 来存储余数 。
因为计算机不像人那么聪明,所以一位一位来 。
首先看1,1除11不够,商0,余1 。
再看2,因为现在余1,所以变成10+2,12除11够,商1,余1,结束 。
我们就可以得到 \(r=r×10+A_i~mod~B\) , \(C_i=r×10+A_i~/B\) 。
注意因为我们除法是从前往后的,所以去除前导0的时候要先进行翻转 。
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
vector<int> div(vector<int>& A, int& B,int& r)
{
r=0; vector<int> C;
for(int i=A.size()-1;i>=0;i--)
{
r=r*10+A[i];
C.push_back(r/B);
r%=B;
}
reverse(C.begin(),C.end());
while(C.size()>1&&C.back()==0) C.pop_back();
return C;
}
int main()
{
string a;
cin>>a;
vector<int> A; int B;
cin>>B;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
int r;
vector<int> C=div(A,B,r);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];
cout<<endl;
cout<<r;
}
因为数组一个位置可以存很大的数,所以我们只存一个数有点浪费,所以我们可以使用压位这个技巧 。
我们先开两个全局变量 \(N=x\) \(M=10^x\) (x为你想压的位数) 。
输入的时候改成 。
int st=max(0,1-N+1),len=i-st+1;
A.push_back(a.substr(st,len)-'0');
注意所有 \(/10~mod~10\) 的操作均要修改成 \(/M~mod~M\) 输出的时候要先输出首位,因为输出的时候有可能不一定正好是 \(x\) 位数,要使用 printf("%04d",C[i]) (以4位举例) 所以当输出完首位后,从 C.size()-2 开始 。
首先,我们先算出整数部分,因为计算机不像手算,直接算出就可以了,不需要像手算一样一位一位算 。
然后我们回想一下手算是怎么算小数部分的,我们将上一位算得的余数乘10再除以除数就行了 。
示意图 。
//c是位数
digit[0]=a/b;
a%=b;
for(int i=1;i<=c+1;i++)
{
a*=10;
digit[i]=a/b;
a%=b;
}
cout<<digit[0]<<'.';
for(int i=1;i<=c;i++) cout<<digit[i];
由于小数会出现循环,所以题目一般会给出一些要求,例如最后一位四舍五入 。
四舍五入即为 当数 \(>=5\) 进一位, \(<5\) 时舍去 。
不过我们需要注意,进位的时候可能会出现“多米诺骨牌” 。
例如 9.99999... 进位后会变成10.00000....,所以我们需要使用循环来处理 。
注意进位到整数位如果变成10不用管,因为我们是直接输出整数位的 。
if(digit[c+1]>=5) digit[c]++;
for(int i=c;i>0;i--)
{
if(digit[i]>=10)
{
digit[i]-=10;
digit[i-1]++;
}
}
因为8进制小数能完美转换成10进制小数,所以我们就不用管保留几位的问题 。
举个例子,0.75如何转换成10进制呢?
\(7/8^1+7/8^2=0.953125\) 这样就转换完成了,不会的去补初一数学 。
于是我们就可以模拟手算(见上文保留?位),再用数组进行存储就行了,因为题目也给出了范围 \(0<k<15\) ,因为所有小数点后位数为n的八进制小数都可以表示成小数点后位数不多于3n的十进制小数。所以我们数组长度开个50就行了 。
还要注意进位的问题,与之前一样都是”多米诺骨牌“,所以要用循环 。
还要开一个变量,记录数组里面一共有多少位了方便输出 。
坑点:
pow 返回的是 double 类型,所以要进行强转,因为 \(3^1\) \(^5\) 非常大,使用 long long ,所以 与它进行计算的 \(x\) 也必须是 long long 类型 。
答案有可能变成1点几,例如当输入为 0.898 时,网上很多人都没有注意到这一点,为此我们从数组的第一位开始,第0位专门留着防止进位变1 。
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
int digit[50];
int main() {
string a;
cin >> a;
int t = 0;
for (int i = 2; i <= a.size() - 1; i++) {
ll x = a[i] - '0';
int j = 0;
while (x) {
x *= 10;
digit[++j] += x / (ll)pow(8, i - 1);
int k = j;
while (digit[k] >= 10) {
digit[k] -= 10;
digit[--k]++;
}
x %= (ll)pow(8, i - 1);
}
t = max(t, j);
}
cout << a << " [8] = " << digit[0] << '.';
for (int i = 1; i <= t; i++) cout << digit[i];
cout << " [10]";
return 0;
}
首先我们分析一下题目是什么意思,相信大家在上幂这一课,老师都讲过这个故事 。
第一个格子有 \(2^0\) 粒米,第二个格子有 \(2^1\) 粒米,…… 。
我们在这里直接从1开始,不然全是0,第20个格子就遍历19次 。
注意这题的说法略微有些歧义,从第 \(n\) 个到第 \(m\) 个,应该第一次从 1 遍历到 \(n-1\) 第二次从 1 遍历到 \(m\) ,然后将两次结果一减就行了,注意也要使用高精 。
接下来就是三位一撇的问题了,我是先将正序的结果存储下来,然后再根据位数对3进行取模,注意最后一位不要有逗号 。
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
vector<int> mul(vector<int>& A,int B)
{
int t=0;
vector<int> C;
for(int i=0;i<A.size();i++)
{
t+=A[i]*B;
C.push_back(t%10);
t/=10;
}
while(t)
{
C.push_back(t%10);
t/=10;
}
return C;
}
vector<int> sub(vector<int>& A,vector<int>& B)
{
int t=0;
vector<int> C;
for(int i=0;i<A.size();i++)
{
t=A[i]-t;
if(i<B.size()) t-=B[i];
C.push_back((t+10)%10);
if(t<0) t=1;
else t=0;
}
while(C.size()>1&&C.back()==0) C.pop_back();
return C;
}
int main()
{
int n,m;
cin>>n>>m;
vector<int> C,D,E,F;
C.push_back(1); D.push_back(1);
for(int i=1;i<n;i++) C=mul(C,2);
for(int i=1;i<=m;i++) D=mul(D,2);
E=sub(D,C);
for(int i=E.size()-1;i>=0;i--) F.push_back(E[i]);
int cnt=0;
if(E.size()%3==0) cnt=3;
else cnt=E.size()%3;
for(int i=0;i<F.size();i++)
{
cout<<F[i];
if(i==cnt-1&&i!=F.size()-1) cout<<',',cnt+=3;
}
return 0;
}
我们可以发现这题就是高精度乘低精度 我们直接将上一次得到的结果再乘当前的数就行了 。
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
vector<int> mul(vector<int>& A,int B)
{
int t=0;
vector<int> C;
for(int i=0;i<A.size();i++)
{
t+=A[i]*B;
C.push_back(t%10);
t/=10;
}
while(t)
{
C.push_back(t%10);
t/=10;
}
return C;
}
int main()
{
int B;
cin>>B;
vector<int> A; A.push_back(1);
for(int i=B;i>=1;i--) A=mul(A,i);
for(int i=A.size()-1;i>=0;i--) cout<<A[i];
}
最后此篇关于高精度加减乘除小数详解的文章就讲到这里了,如果你想了解更多关于高精度加减乘除小数详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我需要帮助返回表达式的值(表达式是函数的参数)。 这是我目前尝试过的。这只是一个随机方程的例子,我的计划是了解如何正确求解它,以便稍后将其转换为函数 sum = 0 eq = '2+4-5' stri
您好,我正在尝试计算按键时一行中多个字段的总和(我的代码将包含多行)(其中一些使用加法,一些使用减法)但由于我是新手,我对 JQuery 不太熟悉。在我的场景中,B.Salary、Rec.comm、S
所以它应该做的是能够接收一个 const char* str 并将其更改为一个 int,然后将其转换回一个字符串以供输出。但它也应该能够将这些加减在一起。我正在通过我的前两个测试,但我的添加发生了一些
这个问题在这里已经有了答案: Retain precision with double in Java (24 个回答) 关闭9年前。 所以当我在 Java 中使用 Doubles 进行加法或减法时,
Java 的开发过程中免不了与 Date 类型纠缠,准备总结一下项目经常使用的日期相关操作,JDK 版本 1.7,如果能够帮助大家节约那么几分钟起身活动一下,去泡杯咖啡,便是极好的,嘿嘿。当然,我
我将用户提供的 html 保存在数据库中,“指定或不指定”css 字体大小。 并尝试制作一个按钮来放大或减小 dom 上的字体大小,但保持相对大小。 我将变量 def_size 定义为 16px,并将
我有一个看起来很容易解决的问题,但是我不确定语法。 我需要运行 if/else 语句,但我不确定如何正确设置条件。 错误代码: if (float_a = float_b or is within +
刚开始学习 C#。我计划将它用于繁重的数学模拟,包括数值求解。问题是我在加减 double 以及进行比较时会出现精度损失。代码及其返回的内容(在注释中)如下: namespace ex3 {
我是一名优秀的程序员,十分优秀!