C语言 高精度算法

      笔者去年总结了Pascal里有关高精度计算的问题,首先高精度计算可以解决以下四个问题:
    
    1. 加数,减数,
运算结果的输入和存储:
        
运算因子超出了整型、实型能表示的范围,肯定不能直接用一个数的形式来表示。 在Pascal中,能表示多个数的数据类型有两种:数组和字符串。
 数组:每个数组元素存储1位(在优化时,这里是一个重点!),有多少位就需要多少个数组元素;用数组表示数的优点:每一位都是数的形式,可以直接加减;运算时非常方便。用数组表示数的缺点:数组不能直接输入;输入时每两位数之间必须有分隔符,不符合数值的输入习惯;
字符串:String型字符串的最大长度是255,可以表示255位。Ansistring型字符串长度不受限制。用字符串表示数的优点:能直接输入输出,输入时,每两位数之间不必分隔符,符合数值的输入习惯;用字符串表示数的缺点:字符串中的每一位是一个字符,不能直接进行运算,必须先将它转化为数值再进行运算;运算时非常不方便;
综合以上所述,对上面两种数据结构取长补短:用字符串读入数据,用数组存储数据。
    
    2. 
运算过程:
       
(1) 运算顺序 :两个数靠右对齐;从低位向高位运算;先计算低位再计算高位;
(2)运算规则:同一位的两个数相加再加上从低位来的进位,成为该位的和;这个和去掉向高位的进位就成为该位的值;如上例:3+8+1=12,向前一位进1,本位的值是2;可借助MOD、DIV运算完成这一步;
(3)最后一位的进位:如果完成两个数的相加后,进位位值不为0,则应添加一位;
(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

       循环完成后,我们还需要进行最后一步,因为两个最高位相加仍可能产生进位,所以,我们在这里还要额外做一次对 j 的判断,如果 j 值为 1 的话, Cn+1 应该要进位,即 Cn+1 = 1。



评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值