荔园在线
荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀
[回到开始]
[上一篇][下一篇]
发信人: OneSecond (别问我是谁), 信区: ACMICPC
标 题: 高精度 --nju huhaiming
发信站: 荔园晨风BBS站 (Fri Jul 30 20:36:15 2004), 站内信件
发信人: huhaiming (一生只爱她), 信区: Program
标 题: 高精度 --nju
发信站: 荔园晨风BBS站 (Sun May 11 11:30:50 2003), 转信
高精度计算
由于计算机输入计算结果的精度通常受到计算机的限制,如:在双精度方式下,计算机最
多只能输出16位有效数字,如果超过16位,则只能按浮点形式输出,另外,一般计算机实
数表示的范围为1038,如果超过这个范围,计算机就无法表示了。但是我们可以通过一些
简单的办法来解决这个问题。这就是我们要说的高精度计算机。
一、 基本方法:
在计算机上进行高精度计算,首先要处理好以下几个基本问题:
1、 数据的接收与存储;
2、 计算结果位数的确定;
3、 进位处理和借位处理;
4、 商和余数的求法;
下面我们逐一介绍一下这几个问题的解决方法。
1. 数据的接收与存储:
要在计算机上进行高精度计算,首先就应该有精确的输入,即计算机要精确地接收和存储
数据。通常:
①、当输入的数值在计算机允许的范围内时,可以用数值型变量来接收数据。
②、当输入的数据超过计算机允许显示的精度范围时,采用字符来接收数据。
③、分离各位数字。
接收数据子模块(字符型变量接收数据):
prucedure readdata(var in:array[1..100] of integer);
var ch:char;
i,k:integer;
begin
read(ch);k:=0;
while ch in['0'..'9'] do begin
inc(k);int[k]:=ord(ch)-48;
read(ch);
end;
end;
2. 计算结果位数的确定
①、 两数之和的位数最大为较大的数的位数加1。
②、 乘积的位数最大为两个因子的位数之和。
③、 阶乘与乘方的位数可以采用对数运算来确定计算结果的位数
。
3. 进位处理和借位处理
①、 加法的进位处理
进行加法处理时,先设置一个加法进位标志 T,并将 T 的初值设为 0。当两数相加时,从
低位到高位,各位数字分别相加,如果相加后某个单元中的数大于 10,则将该单元中的数
减去10,并将进位标志 T 设为 1,当对下一单元进行相加时,还要再加上前一个单元的进
位标志 T。同时将 T 再次置为 0,不断重复,直到最高位为止。具体算法为:
T:=0;
对变量 I 从 1 到 N,重复下列步骤:
C[I]:=A[I]+B[I]+T;T:=0;
IF C[I]>=10 THEN BEGIN
C[I]:=C[I]-10;T:=1;
END;
②、 乘法的进位处理
Y:=A[I]*B[I]+C;C:=Y div 10;C[I+J-1]:=Y-C*10
③、 减法的借位处理
IF A[I]<B[I] THEN BEGIN
A[I+1]:=A[I+1]-1;
A[I]:=A[I]+10
END IF
C[I]:=A[I]-B[I];
④、 商和余数的求法
设A,B分别为不大于9位的整数,则:
C:=A DIV B为商的整数部分
X:=A MOD B为余数
二、 算法与实例:
1. 求任意位数的加法运算
数据的接收和存储
采用字符串输入的方式,设参与运算的两个数分别为A和B,利用字符串函数把字符串转化
为数值,将A、B中的每一位数字分别存储在A、B两个数组中,最低位在第一个单元中。(P
ASCAL语言中可以直接采用字符读取的方式来接收数据,而后通过ORD(x)-48的方式转化成
数值。)
确定和的位数
设LA为A的位数,LB为B的位数,则两数之和的位数最大为较大加数位数加1,即如果LA>LB
,则和的位数最大为LA+1。
进位处理
进行加法处理时,先设置一个加法进位标志 T,并将 T 的初值设为 0。当两数相加时,从
低位到高位,各位数字分别相加,如果相加后某个单元中的数大于 10,则将该单元中的数
减去10,并将进位标志 T 设为 1,当对下一单元进行相加时,还要再加上前一个单元的进
位标志 T。同时将 T 再次置为 0,不断重复,直到最高位为止。
程序清单:
program gjdjs;
const n=100;
type arrtype=array[1..n] of integer;
var
a,b:arrtype;
t,s,j,l:integer;
procedure readdata(var int:arrtype);
var
ch:char;
i,k:integer;
begin
writeln('Input a number:');
read(ch);k:=0;
while ch in['0'..'9'] do begin
inc(k);
int[k]:=ord(ch)-48;
read(ch);
end;
for i:=k downto 1 do begin
int[n+i-k]:=int[i];
int[i]:=0;
end;
end;
begin
readdata(a);readln;
readdata(b);writeln;
t:=0;
for j:=n downto 1 do begin
s:=a[j]+b[j]+t;
a[j]:=s mod 10;
t:=s div 10;
end;
j:=1;writeln('output:');
while a[j]=0 do j:=j+1;
while j<=n do begin
write(a[j]);
inc(j);
end;
writeln;
end.
2. 求任意位数的减法运算
数据的接收和存储
采用字符串输入的方式,设参与运算的两个数分别为A和B,利用字符串函数把字符串
转化为数值,将A、B中的每一位数字分别存储在A、B两个数组中,最低位在第一个单元中
。(PASCAL语言中可以直接采用字符读取的方式来接收数据,而后通过ORD(x)-48的方式转
化成数值。)
确定差的位数
设LA为A的位数,LB为B的位数,则两数之差的位数最大为较大数的位数,即如果LA>L
B,则差的位数最大为LA。
借位处理
做减法运算时,要先判断是否需要借位,如果需要借位,从上一位借过一个10,上一
位的数减去1,处理完之后再相减。
负数的处理
如果减数大于被减数,则交换A、B的值,并令负数标志 T=-1。当打印计算结果时,先
判断 T 的值是否为-1,如果T=-1,则在数值前面先输出一个负号。
程序清单:
program gjdjs;
const n=100;
type arrtype=array[1..n] of integer;
var
a,b:arrtype;
g,t,j,l:integer;
s:char;
f:boolean;
procedure readdata(var int:arrtype);
var
ch:char;
i,k:integer;
begin
writeln('Input a number:');
read(ch);k:=0;
while ch in['0'..'9'] do begin
inc(k);
int[k]:=ord(ch)-48;
read(ch);
end;
for i:=k downto 1 do begin
int[n+i-k]:=int[i];
int[i]:=0;
end;
end;
begin
readdata(a);readln;
readdata(b);writeln;
f:=true;j:=1;
while (a[j]=b[j]) and (j<n) do j:=j+1;
if a[j]<b[j] then f:=false;
if f then s:=' ' else begin
s:='-';
for l:=1 to n do begin
t:=a[l];a[l]:=b[l];b[l]:=t;
end;
end;
t:=0;
for j:=n downto 1 do
if a[j]>=b[j]+g then begin
a[j]:=a[j]-b[j]-g;
g:=0;
end
else begin
a[j]:=10+a[j]-b[j]-g;
g:=1;
end;
j:=1;writeln('output:');write(s);
while (a[j]=0) and (j<n) do j:=j+1;
while j<=n do begin
write(a[j]);
inc(j);
end;
writeln;
end.
3. 求任意位数的乘法运算
数据的接收和存储
采用字符串输入的方式,设参与运算的两个数分别为A和B,利用字符串函数把字符串
转化为数值,将A、B中的每一位数字分别存储在A、B两个数组中,最低位在第一个单元中
。(PASCAL语言中可以直接采用字符读取的方式来接收数据,而后通过ORD(x)-48的方式转
化成数值。)
确定积的位数
设LA为A的位数,LB为B的位数,乘积的位数最多为LA+LB,最少为LA+LB-1。所以,乘
积的位数上限为LA+LB。
算法
首先计算被乘数与乘数的个位数字的乘积,把结果保存到积数组中,然后再用被乘数
去乘以乘数的十位数字,把结果退一位加到积数组中。每加一次乘积结果就进行一次进位
处理,其方法与加法中的进位处理一样。
程序清单:
program gjdcs;
const n=100;
type arrtype=array[0..n] of integer;
arrtype2=array[1..2*n] of integer;
var
a,b,d:arrtype;
c:arrtype2;
g,i,j,s:integer;
procedure readdata(var int:arrtype);
var
ch:char;
i,k:integer;
begin
writeln('Input a number:');
read(ch);k:=0;
while ch in['0'..'9'] do begin
inc(k);
int[k]:=ord(ch)-48;
read(ch);
end;
for i:=k downto 1 do begin
int[n+i-k]:=int[i];
int[i]:=0;
end;
end;
begin
readdata(a);readln;
readdata(b);writeln;
for i:=n downto 1 do begin
g:=0;
for j:=n downto 1 do begin
s:=a[j]*b[i]+g;
d[j]:=s mod 10;
g:=s div 10;
end;
d[0]:=s;
g:=0;
for j:=n downto 0 do begin
s:=d[j]+c[i+j]+g;
c[i+j]:=s mod 10;
g:=s div 10
end;
end;
j:=1;writeln('output:');
while (c[j]=0) and (j<2*n) do j:=j+1;
while j<=2*n do begin
write(c[j]);
inc(j);
end;
writeln;
end.
4. 求A÷B的精确值
算法
由于A和B都是计算机允许的显示精度,故A、B均可使用数值变量来接收与存储。A÷B的精
确值有两种情况:
①、 A能被B整除,没有余数。
②、 A不能被B整除,对余数进行处理。
首先我们知道,在做除法运算时,有一个不变的量和三个变化的量,不变的量是除数,三
个变化的量分别是:被除数、商和余数。做除法运算时,每次都是用被除数减去商与除数
的积,如果所得余数不为零,则将其扩大10倍再次作为被除数,继续试除,直至余数为零
或达到要求的精确度为止。最后,必须对精确度的后一位求商,然后判断其值而四舍五入
得出最后的结果。
程序清单:
program jqdcf;
const n=100;
type arrtype=array[0..n] of integer;
var
a,b,c:arrtype;
i,j,k,l,m:integer;
begin
write('Input a number:');readln(m);
write('Input a number:');readln(k);
write('Input a accuracy:');readln(l);
write(m,'/',k,'=');
a[0]:=m;
b[0]:=m div k;
c[0]:=m mod k;
if c[0]=0 then write(b[0])
else begin
for i:=1 to l+1 do begin
a[i]:=c[i-1]*10;
b[i]:=a[i] div k;
c[i]:=a[i] mod k;
if c[i]=0 then i:=l+1;
end;
if b[l+1]>4 then b[l]:=b[l]+1;
for i:=l downto 1 do
if b[i]>=10 then begin
b[i]:=b[i]-10;
b[i-1]:=b[i-1]+1;
end
else
i:=1;
write(b[0],'.');
for i:=1 to l do write(b[i]);
end;
readln;
end.
5. 求多精度A÷单精度B的商和余数。
数据的接收和存储
采用字符串输入的方式,设参与运算的两个数分别为A和B,利用字符串函数把字符串转化
为数值,将A中的每一位数字分别存储在A数组中,最低位在第一个单元中。(PASCAL语言中
可以直接采用字符读取的方式来接收数据,而后通过ORD(x)-48的方式转化成数值。)。B则
可以用一般的整数变量来接收和存储。
算法
首先,我们知道,在做除法运算时,有一个不变的量和三个变化的量,不变的量是除数,
三个变化的量分别是:被除数、商和余数。做除法运算时,每次都是用被除数减去商与除
数的积,如果所得余数不为零,则将其扩大10倍再次作为被除数,继续试除,直至余数为
零或达到要求的精确度为止。
程序清单:
program gjdcydjd;
const n=100;
type arrtype=array[0..n] of integer;
var
a:arrtype;
g,b,i,j,s:integer;
procedure readdata(var int:arrtype);
var
ch:char;
i,k:integer;
begin
write('Input a number:');
read(ch);k:=0;
while ch in['0'..'9'] do begin
inc(k);
int[k]:=ord(ch)-48;
read(ch);
end;
for i:=k downto 1 do begin
int[n+i-k]:=int[i];
int[i]:=0;
end;
end;
begin
readdata(a);writeln;
write('Input the numbr b:');
readln(b);writeln;
g:=0;
for i:=1 to n do begin
s:=a[i]+g*10;
a[i]:=s div b;
g:=s mod b;
end;
j:=1;writeln('output:');
while (a[j]=0) and (j<n) do j:=j+1;
while j<=n do begin
write(a[j]);
inc(j);
end;
if g<>0 then writeln('......',g);
writeln;
end.
6. 求多精度A÷多精度B的商和余数。
数据的接收和存储
采用字符串输入的方式,设参与运算的两个数分别为A和B,利用字符串函数把字符串转化
为数值,将A、B中的每一位数字分别存储在A和B数组中,最低位在第一个单元中。(PASCA
L语言中可以直接采用字符读取的方式来接收数据,而后通过ORD(x)-48的方式转化成数值
。)。
算法
可以用减法代替除法运算:不断比较A[1..n]与B[1..n]的大小,如果A[1..n]>=B[1..n]则
商C[1..n]+1→C[1..n],然后就是一个减法过程:A[1..n]-B[1..n]→A[1..n]。由于简单
的减法速度太慢,故必须进行优化。设置一个位置值J,当A[1..n]>B[1..n]时。B[1..n]左
移→B[0..n],j:=j+1,即令B[1..n]增大10倍。这样就减少了减法的次数。当j>0且A[1..
n]<B[0..n]时,B[0..n]右移→B[0..n],j:=j-1,即令B[1..n]缩小10倍。
程序清单:
program gjdcygjd;
const n=100;
type arrtype=array[1..n] of integer;
var
a,b,c:arrtype;
g,s,i,j,k,l:integer;
procedure readdata(var int:arrtype);
var
ch:char;
i,k:integer;
begin
write('Input a number:');
read(ch);k:=0;
while ch in['0'..'9'] do begin
inc(k);
int[k]:=ord(ch)-48;
read(ch);
end;
for i:=k downto 1 do begin
int[n+i-k]:=int[i];
int[i]:=0;
end;
end;
function f(a,b:arrtype):boolean;
var j:integer;
begin
f:=true;j:=1;
while (a[j]=b[j]) and (j<n) do j:=j+1;
if a[j]<b[j] then f:=false
end;
procedure sub;
var
i:integer;
begin
g:=0;
for i:=n downto 1 do
if a[i]>=b[i]+g then
begin
a[i]:=a[i]-b[i]-g;
g:=0;
end
else
begin
a[i]:=10+a[i]-b[i]-g;
g:=1;
end;
end;
procedure ine(n:integer);
var i,s:integer;
begin
g:=0;
c[n]:=c[n]+1;
for i:=n downto 1 do begin
s:=c[i]+g;
c[i]:=s mod 10;
g:=s div 10;
end;
end;
begin
readdata(a);readln;
readdata(b);writeln;
j:=1;
while f(a,b) do begin
j:=j+1;
for i:=2 to n do b[i-1]:=b[i];
b[n]:=0;
end;
while j>0 do begin
while f(a,b) do begin
ine(n-j+1);
sub;
end;
j:=j-1;
for i:=n downto 2 do begin
b[i]:=b[i-1];
b[i-1]:=0;
end;
end;
j:=1;writeln('output:');
while (c[j]=0) and (j<n) do j:=j+1;
while j<=n do begin
write(c[j]);
inc(j);
end;
j:=1;
while (a[j]=0) and (j<=n) do j:=j+1;
if j<=n then write('......');
while j<=n do begin
write(a[j]);
inc(j);
end;
writeln;
end.
--
※ 修改:·huhaiming 於 May 11 11:34:19 修改本文·[FROM: 192.168.0.200]
※ 转载:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.0.200]
[回到开始]
[上一篇][下一篇]
荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店