笔者去年总结了Pascal里有关高精度计算的问题,首先高精度计算可以解决以下四个问题:
1. 加数,减数, 运算结果的输入和存储:
运算因子超出了整型、实型能表示的范围,肯定不能直接用一个数的形式来表示。 在Pascal中,能表示多个数的数据类型有两种:数组和字符串。
1. 加数,减数, 运算结果的输入和存储:
运算因子超出了整型、实型能表示的范围,肯定不能直接用一个数的形式来表示。 在Pascal中,能表示多个数的数据类型有两种:数组和字符串。
数组:每个数组元素存储1位(在优化时,这里是一个重点!),有多少位就需要多少个数组元素;用数组表示数的优点:每一位都是数的形式,可以直接加减;运算时非常方便。用数组表示数的缺点:数组不能直接输入;输入时每两位数之间必须有分隔符,不符合数值的输入习惯;
字符串:String型字符串的最大长度是255,可以表示255位。Ansistring型字符串长度不受限制。用字符串表示数的优点:能直接输入输出,输入时,每两位数之间不必分隔符,符合数值的输入习惯;用字符串表示数的缺点:字符串中的每一位是一个字符,不能直接进行运算,必须先将它转化为数值再进行运算;运算时非常不方便;
综合以上所述,对上面两种数据结构取长补短:用字符串读入数据,用数组存储数据。
2. 运算过程:
(1) 运算顺序 :两个数靠右对齐;从低位向高位运算;先计算低位再计算高位;
笔者大一的时候学了一种新的编程语言叫做C语言,相信很多同学都已经接触过C语言了,今天在xiaoz吧看到某无良吧主发的题目,我就说这题目不是一般a+b,这家伙还一直狡辩,现在我来跟大家分享一下有关高精度计算的C语言算法。
不多说,当然先上无良吧主今日分享的题目,见下图:
Double 变量以带符号的 IEEE 64 位(8 个字节)双精度浮点数形式存储,负值取值范围为 -1.79769313486231570E+308 到 -4.94065645841246544E-324,正值取值范围为 4.94065645841246544E-324 到 1.79769313486231570E+308。
long int 的取值范围为 -2147483648至2147483647,很显然,是不能用long int的,如果使用long int,则数据溢出。
如果使用double型,则运算速率会非常慢。大家可以试试。最好让无良的吧主去试试。烦死他!
OK,现在,我来说说这道题目的正确算法。这道题目明显就是必须要用高精度计算。
因为是任意长的二整数求和,而系统中整型数的精度有限,因此需用字符串形式存储整数,并按位逐 位进行运算,运算过程中需考虑进位和借位的问题。再者,整数可以是负数,若二整数符号相同则为加法运 算,相异则实为减法运算,若是减法运算还需比较二数的绝对值大小,以确定最终结果的符号及减数与被 减数的安排顺序。
无良吧主出的出的拿到题目正确算法如下:(大家可以任意输入数字检验)
+B1 B2 B3 B4
---------------------
C0 C1 C2 C3 C4
2. 运算过程:
(1) 运算顺序 :两个数靠右对齐;从低位向高位运算;先计算低位再计算高位;
(2)运算规则:同一位的两个数相加再加上从低位来的进位,成为该位的和;这个和去掉向高位的进位就成为该位的值;如上例:3+8+1=12,向前一位进1,本位的值是2;可借助MOD、DIV运算完成这一步;
(3)最后一位的进位:如果完成两个数的相加后,进位位值不为0,则应添加一位;
(4)如果两个加数位数不一样多,则按位数多的一个进行计算。
3. 结果的输出(这也是优化的一个重点): 按运算结果的实际位数输出
4. 优化:
3. 结果的输出(这也是优化的一个重点): 按运算结果的实际位数输出
4. 优化:
(1)浪费空间:一个整型变量(-32768~32767)只存放一位(0~9);
(2)浪费时间:一次加减只处理一位;
之前写的高精度加法计算Pascal代码如下:(可以根据上面高精度计算能解决的四个问题来理解)
program test;
type
my_arr=array [0..100] of longint;
var
str1,str2:string;
d1,d2,d3:my_arr;
procedure replace(str:string;var arr:my_arr);
var
i,j:longint;
begin
arr[0]:=length(str);
for i:=1 to arr[0] do
begin
arr[i]:=ord(str[arr[0]-i+1])-ord('0');
end;
end;
function max(i,j:longint):longint;
begin
if i>j then exit(i);
exit(j);
end;
procedure add_arr(arr1,arr2:my_arr;var arr3:my_arr);
var
i,j,k,t:longint;
begin
fillchar(arr3,sizeof(arr3),0);
k:=max(arr1[0],arr2[0]);
for i:=1 to k do
begin
t:=(arr1[i]+arr2[i]+arr3[i]);
arr3[i]:=t mod 10;
arr3[i+1]:=t div 10;
end;
if arr3[k+1]<>0 then
arr3[0]:=k+1
else
arr3[0]:=k;
end;
procedure print_arr(arr:my_arr);
var
i:longint;
begin
if arr[0]=0 then
begin
writeln(0);
exit;
end;
for i:=arr[0] downto 1 do
write(arr[i]);
writeln;
end;
begin
readln(str1);
replace(str1,d1);
readln(str2);
replace(str2,d2);
add_arr(d1,d2,d3);
print_arr(d3);
end.
笔者大一的时候学了一种新的编程语言叫做C语言,相信很多同学都已经接触过C语言了,今天在xiaoz吧看到某无良吧主发的题目,我就说这题目不是一般a+b,这家伙还一直狡辩,现在我来跟大家分享一下有关高精度计算的C语言算法。
不多说,当然先上无良吧主今日分享的题目,见下图:
首先我来分析一下这道题目,这道题目的case2:112233445566778899+998877665544332211=1111111111111111110,看到这个数字很多同学就开始想了,我能不能用long int或者double型呢?
Double 变量以带符号的 IEEE 64 位(8 个字节)双精度浮点数形式存储,负值取值范围为 -1.79769313486231570E+308 到 -4.94065645841246544E-324,正值取值范围为 4.94065645841246544E-324 到 1.79769313486231570E+308。
long int 的取值范围为 -2147483648至2147483647,很显然,是不能用long int的,如果使用long int,则数据溢出。
如果使用double型,则运算速率会非常慢。大家可以试试。最好让无良的吧主去试试。烦死他!
OK,现在,我来说说这道题目的正确算法。这道题目明显就是必须要用高精度计算。
因为是任意长的二整数求和,而系统中整型数的精度有限,因此需用字符串形式存储整数,并按位逐 位进行运算,运算过程中需考虑进位和借位的问题。再者,整数可以是负数,若二整数符号相同则为加法运 算,相异则实为减法运算,若是减法运算还需比较二数的绝对值大小,以确定最终结果的符号及减数与被 减数的安排顺序。
无良吧主出的出的拿到题目正确算法如下:(大家可以任意输入数字检验)
#include<stdio.h>
#include<string.h>
#define MAX 1000 /*宏定义,下面遇到MAX的时候都变成1000 */
int main(void)
{
unsigned char arr1[MAX]={0},arr2[MAX]={0};
int len,i,len1,len2,t,j = 0;
char str[MAX]; /*读入数据,并进行预处理(计算出数字位数,并反方向存放)*/
scanf("%s",str);
len1 = strlen(str);
for(i=0; i<len1; ++i)
arr1[i] = str[len1-1-i] - '0';
scanf("%s",str);
len2 = strlen(str);
for(i=0; i<len2; i++)
arr2[i] = str[len2-1-i] - '0';
len = (len1>len2)?len1:len2;
for(i=0; i<len; i++)
{
t = arr1[i] + arr2[i] +j;
arr1[i] = t % 10;
j = t / 10;
}
if(j != 0) arr1[len++] = j;
for(i=0; i<len; ++i)
printf("%d",arr1[len-1-i]);
return 0;
}
算法的思路:
模拟我们在小学所学的笔算
(对于不足位用0补齐,如9818+13,我们认为是9818+0013),
假设我们要计算的两数分别是 A1 A2 A3 A4 、 B1 B2 B3 B4。
那么我们笔算时,会列出这样的式子
A1 A2 A3 A4
+B1 B2 B3 B4
---------------------
C0 C1 C2 C3 C4
那么我们在计算机中也同样可以完成这个过程。为了方便进位,我们在计算前,将两个数反向储存至数组中,即我们是对 B4 B3 B2 B1 、A4 A3 A2 A1 进行计算。初始化时 j = 0,其中有恒等式 Cn = (An + Bn + j)mod 10 (j为进位结果),根据这个等式我们只需要循环执行以下三步,就能得到大致结果:
1.
temp = An + Bn +j
2. Cn = temp mod 10
3. j = temp div 10
2. Cn = temp mod 10
3. j = temp div 10
循环完成后,我们还需要进行最后一步,因为两个最高位相加仍可能产生进位,所以,我们在这里还要额外做一次对 j 的判断,如果 j 值为 1 的话, Cn+1 应该要进位,即 Cn+1 = 1。