高精度计算

高精度计算_6分词条

目录 [隐藏]

高精度计算 概述

 

所谓的高精度运算,是指参与运算的数(加数,减数,因子……)范围大大超出了标准数据类型(整型实型)能表示的范围的运算。例如,求两个200位的数的和。这时,就要用到高精度算法了。在这里,我们先讨论高精度加法。高精度运算主要解决以下三个问题:
一、加数、减数、运算结果的输入和存储
运算因子超出了整型、实型能表示的范围,肯定不能直接用一个数的形式来表示。在Pascal中,能表示多个数的数据类型有两种:数组和字符串。
数组:每个数组元素存储1位(在优化时,这里是一个重点!),有多少位就需要多少个数组元素;用数组表示数的优点:每一位都是数的形式,可以直接加减;运算时非常方便。用数组表示数的缺点:数组不能直接输入;输入时每两位数之间必须有分隔符,不符合数值的输入习惯;
字符串:字符串的最大长度是255,可以表示255位。用字符串表示数的优点:能直接输入输出,输入时,每两位数之间不必分隔符,符合数值的输入习惯;用字符串表示数的缺点:字符串中的每一位是一个字符,不能直接进行运算,必须先将它转化为数值再进行运算;运算时非常不方便;
综合以上所述,对上面两种数据结构取长补短:用字符串读入数据,用数组存储数据:
var s1,s2:string;
    a,b,c:array 【1..260】 of integer;
    i,l,k1,k2:integer;
begin
     write('input s1:');readln(s1);
     write('input s2:');readln(s2);
     {————读入两个数s1,s2,都是字符串类型}
     l:=length(s1);{求出s1的长度,也即s1的位数;有关字符串的知识。}
     k1:=260;
     for i:=l downto 1 do
     begin
          a【k1】:=ord(s1)-48;{将字符转成数值}
          k1:=k1-1;
     end;
     k1:=k1+1;
     {————以上将s1中的字符一位一位地转成数值并存在数组a中;低位在后(从第260位开始),高位在前(每存完一位,k1减1),完后,k1指向最高位}
对s2的转化过程和上面一模一样。
二、运算过程
在往下看之前,大家先列竖式计算35+86。
注意的问题:
(1)运算顺序:两个数靠右对齐;从低位向高位运算;先计算低位再计算高位;
(2)运算规则:同一位的两个数相加再加上从低位来的进位,成为该位的和;这个和去掉向高位的进位就成为该位的值;如上例:3+8+1=12,向前一位进1,本位的值是2;可借助MOD、DIV运算完成这一步;
(3)最后一位的进位:如果完成两个数的相加后,进位位值不为0,则应添加一位;
(4)如果两个加数位数不一样多,则按位数多的一个进行计算;
     if k1>k2 then k:=k1 else k:=k2;
     y:=0;
     for i:=260 downto k do
     begin
         x:=a+b+y;
         c:=x mod 10;
         y:=x div 10;
     end;
     if y<>0 then begin k:=k-1;c【k】:=y; end;
三、结果的输出(这也是优化的一个重点)
按运算结果的实际位数输出
for i:=k to 260 do write(c);
writeln;
例子:求两个数的加法
program sum;
var s,s1,s2:string;
    a,b,c:array 【1..260】 of integer;
    i,l,k1,k2:integer;
begin
     write('input s1:');readln(s1);
     write('input s2:');readln(s2);
         l:=length(s1);
     k1:=260;
     for i:=l downto 1 do
     begin
          a【k1】:=ord(s1)-48;
          k1:=k1-1;
     end;
     k1:=k1+1;
    l:=length(s2);
     k2:=260;
     for I+:=l downto 1 do
     begin
          b【k2】:=ord(s2)-48;
          k2:=k2-1;
     end;
     k2:=k2+1;
    if k1>k2 then k:=k2 else k:=k1;
     y:=0;
     for i:=260 downto k do
     begin
         x:=a+b+y;
         c:=x mod 10;
         y:=x div 10;
     end;
    if y<>0 then begin k:=k-1;c【k】:=y;
end;
     for i:=k to 260 do write(c);
     writeln;
end.
四、优化:
以上的方法的有明显的缺点:
(1)浪费空间:一个整型变量(-32768~32767)只存放一位(0~9);
(2)浪费时间:一次加减只处理一位;
针对以上问题,我们做如下优化:一个数组元素存放四位数;(integer的最大范围是32767,5位的话可能导致出界)。具体方法:
    l:=length(s1);
     k1:=260;
     repeat  {————有关字符串的知识}
          s:=copy(s1,l-3,4);
          val(s,a【k1】,code);
          k1:=k1-1;
          s1:=copy(s1,1,l-4);
          l:=l-4;
     until l<=0;
     k1:=k1+1;
而因为这个改进,算法要相应改变:
(1)运算时:不再逢十进位,而是逢万进位(mod 10000; div 10000);
(2)输出时:最高位直接输出,其余各位,要判断是否足够4位,不足部分要补0;例如:1,23,2345这样三段的数,输出时,应该是100232345而不是1234567。
改进后的算法:
program sum;
var s1,s2,s:string;
    a,b,c:array 【1..260】 of integer;
    i,l,k1,k2,code:integer;
begin
     write('input s1:');readln(s1);
     write('input s2:');readln(s2);
     l:=length(s1);
     k1:=260;
     repeat  {————有关字符串的知识}
          s:=copy(s1,l-3,4);
          val(s,a【k1】,code);
          k1:=k1-1;
          s1:=copy(s1,1,l-4);
          l:=l-4;
     until l<=0;
     k1:=k1+1;
     l:=length(s2);
     k2:=260;
     repeat
          s:=copy(s2,l-3,4);
          val(s,b【k2】,code);
          k2:=k2-1;
          s2:=copy(s2,1,l-4);
          l:=l-4;
     until l<=0;         
     k2:=k2+1;
    if k1<k2 then k:=k1 else k:=k2;
     y:=0;
     for i:=260 downto k do
     begin
         x:=a+b+y;
         c:=x mod 10000;
         y:=x div 10000;
    end;
    if y<>0 then begin k:=k-1;c【k】:=y;end;
write(c【k】);
    for i:=k+1 to 260 do
    begin
      if c<1000 then write('0');
      if c<100 then write('0');
      if c<10 then write('0');
      write(c);
    end;
    writeln;
end.
减法:和高精度加法相比,减法在差为负数时处理的细节更多一点:当被减数小于减数时,差为负数,差的绝对值是减数减去被减数;在程序实现上用一个变量来存储符号位,用另一个数组存差的绝对值。
2、算法流程:
(1)读入被减数S1,S2(字符串);
(2)置符号位:判断被减数是否大于减数:大则将符号位置为空;小则将符号位置为“-”,交换减数与被减数;
(3)被减数与减数处理成数值,放在数组中;
(4)运算:
        A、取数;
        B、判断是否需要借位;
        C、减,将运算结果放到差数组相应位中;
        D、判断是否运算完成:是,转5;不是,转A;
(5)打印结果:符号位,第1位,循环处理第2到最后一位;
3、细节:
▲如何判断被减数与减数的大小:字符串知识
(1)首先将两个字符串的位数补成一样(因为字符串的比较是从左边对齐的;两个字符串一样长才能真正地比较出大小):短的在左边补0
     k1:=length(s1);
     k2:=length(s2);
     if k1>k2 then for i:=1 to k1-k2 do s2:='0'+s2
              else for i:=1 to k2-k1 do s1:='0'+s1;
(2)接着比较大小:直接比较字符串大小
     fh:='';
     if s1<s2 then begin fh:='-';s:=s1; s1:=s2; s2:=s; end;
         {————s1存被减数,fh存符号}
▲将字符串处理成数值:
l:=length(s1);{求出s1的长度,也即s1的位数;有关字符串的知识。}
     k1:=260;
     for i:=l downto 1 do
     begin
          a【k1】:=ord(s1)-48;{将字符转成数值}
          k1:=k1-1;
     end;
     k1:=k1+1;
▲运算(减法跟加法比较,减法退位处理跟加法进位处理不一样):
  a.处理退位;
    跟加法一样,在for语句外面先将退位清零,
         用被减数再减去退位,
         {注意:由于每一个数位不一定都得向前一位借位,所以这里退位得清零。例如,234-25,个位需借位,而十位不用}
         接着,再判断,当被减数某一位不够减时,则需加上前一位退位过来的数。
         {注意:由于这里采用优化方法,所以退一位,就等于后一位加上10000。)
         最后,再拿一个数组来存储两个减数的差。
   jw:=0;
   for i:=260 downto k1 do
         begin
             a:=a-jw;        {此处jw为下一位从I位的借位}
             jw:=0;                {此处jw为I 位准备向上一位的借位}
if a<b then
begin
                 jw:=1;
                 a:=a+10000;
               end;
           c:=a-b;
         end;

▲打印结果:
   先找到差的第一个非零数,如果差的所有位数都为零,就直接输出零。
   如果不是,就输出符号位和差的第一位。

   剩下部分,打印补足零:
      因为优化后的高精度减法,是把每四个数位分成一段,而每一段则必须有四个
        数,当有一段不足四个数时,就得用"0"补足.(如:第一位是'1',第二位是'34',第三位是'345',第四位是'8', 则应写为'1003403450008').注意:第一位不用补零,(如:第一位为'3',则写成'3').
    while (c【k】=0) and (k<=260) do k:=k+1;
   if k>260 then write('0')
   else begin
write(fh,c【k】);{k是差的第1位;}
      for i:=k+1 to 260 do
      begin
        if c<1000 then write('0');
        if c<100 then write('0');
        if c<10 then write('0');
        write(c);
      end;
end;

参考程序:
program Zfjianfa;
const n=25;
var s1,s2,s3,s4,s:string;
    a,b,c:array【1..n】 of integer;
    i,k1,k2,l,code,jw:integer;
    cq:char;
begin
    readln(s1);
    readln(s2);
    k1:=length(s1);
    k2:=length(s2);
    if k1>k2 then for i:=1 to k1-k2 do s2:='0'+s2
             else for i:=1 to k2-k1 do s1:='0'+s1;
    cq:=' ';
    if s1<s2 then begin cq:='-'; s:=s1; s1:=s2; s2:=s; end;
     l:=length(s1);
     k1:=n;
     repeat
       s3:=copy(s1,l-3,4);
       val(s3,a【k1】,code);
       k1:=k1-1;
       delete(s1,l-3,4);
       l:=l-4;
       until l<=0;
       k1:=k1+1;
     i:=length(s2);
     k2:=n;
     repeat
       s4:=copy(s2,i-3,4);
       val(s4,b【k2】,code);
       k2:=k2-1;
       delete(s2,i-3,4);
       i:=i-4;
     until i<=0;
     k2:=k2+1;
     jw:=0;
     for i:=n downto k1 do
     begin
          a:=a-jw;
          jw:=0;
          if a<b then begin
                              jw:=1;
                              a:=a+10000;
                            end;
          c:=a-b;
     end;
     while (c【k1】=0) and (k1<=n) do
           k1:=k1+1;
     if k1>n then writeln('0')
               else begin
                        write(cq,c【k1】);
                        for i:=k1+1 to n do
                        begin
                             if c<1000 then write('0');
                             if c<100 then write('0');
                             if c<10 then write('0');
                             write(c);
                         end;
                     end;
writeln;
end.
高精度乘法基本思想和加法一样。其基本流程如下:
①读入被乘数s1,乘数s2
②把s1、s2分成4位一段,转成数值存在数组a,b中;记下a,b的长度k1,k2;
③i赋为b中的最低位;
④从b中取出第i位与a相乘,累加到另一数组c中;(注意:累加时错开的位数应是多少位?)
⑤i:=i-1;检测i值:小于k2则转⑥,否则转④
⑥打印结果
参考程序:
program chengfa;
const n=100;
type ar=array 【1..n】 of integer;
var  a,b:ar; k1,k2,k:integer;
     c:array 【1..200】 of integer;
     s1,s2:string;
procedure fenge(s:string;var d:ar; var kk:integer); {将s分割成四位一组存放在d中,返回的kk值指向d的最高位}
var ss:string;
    i,code:integer;
begin
     i:=length(s);
     kk:=n;
     repeat
           ss:=copy(s,i-3,4);
           val(ss,d【kk】,code);
           kk:=kk-1;
           s:=copy(s,1,i-4);
           i:=i-4;
     until i<0;
     kk:=kk+1;
end;
procedure init;
var i:integer;
begin
      for i:=1 to n do begin a:=0; b:=0; end;
      for i:=1 to 2*n do c:=0;
      write('input 2 numbers:');
      readln(s1);
      readln(s2);
      fenge(s1,a,k1);
      fenge(s2,b,k2);
end;
procedure jisuan;
var i,j,m:integer; x,y,z,jw:longint;
begin
      i:=n; k:=2*n;
      repeat
            x:=b; z:=0; m:=k; jw:=0;
            for j:=n downto k1 do
            begin
                  y:=a【j】;
                  z:=c【m】;
                  x:=x*y+z+jw;
                  jw:=x div 10000;
                  c【m】:=x mod 10000;
                  m:=m-1;
                  x:=b;
            end;
            if jw<>0 then c【m】:=jw else m:=m+1;
            i:=i-1;
            k:=k-1;
      until i<k2;
      k:=m;
end;
procedure daying;
var i:integer;
begin
      write(c【k】);
      for i:=k+1 to 2*n do
      begin   
          if c<1000 then write('0');
          if c<100 then  write('0');
          if c<10 then write('0');
          write(c);
      end;
      writeln;
end;
begin
      init;
      jisuan;
      daying;
end.

教材“基础编”P87高精乘法参考程序:
program ex3_1;
var
  a,b,c:array【0..1000】 of word;
procedure init;
var
  s:string;
  ok,i,j:integer;
begin
  readln(s);
  a【0】:=length(s);
  for i:=1 to a【0】 do
    val(s【a【0】-i+1】,a,ok);
  readln(s);
  b【0】:=length(s);
  b【0】:=length(s);
  for i:=1 to b【0】 do
    val(s【b【0】-i+1】,b,ok);
end;
procedure highmul;
var i,j,k:integer;
begin
  c【0】:=a【0】+b【0】;
  for i:=1 to b【0】 do
    for j:=1 to a【0】+1 do
    begin
      inc(c【i+j-1】,a【j】*b mod 10);
      c【i+j】:=c【i+j】+(a【j】*b div 10)+(c【i+j-1】 div 10);
      c【i+j-1】:=c【i+j-1】 mod 10;
    end;
end;
procedure print;
var i:integer;
begin
  while c【c【0】】=0 do dec(c【0】);
  for i:=c【0】 downto 1 do
    write(c);
  writeln;
end;

begin
  init;
  highmul;
  print;
end.

 

高精度计算 相关链接

 

计算机语言

附图

上传图片 

互动百科的词条(含所附图片)系由网友上传,如果涉嫌侵权,请与客服联系,我们将按照法律之相关规定及时进行处理。如需转载,请注明来源于www.hudong.com

被引用: 本词条已被如下媒体引用 我来补充
开放分类: 我来补充

讨论区

更多>>

编辑者

共1人协作

相关词条

plascal教程
c语言程序
Remote Data Objects
远程数据对象
运算器
变量主题
算盘
字符串
RDO
中央处理器
更多

Copyright © 2005-2009 hudong.com Ltd. All Rights Reserved. 互动在线 版权所有