0
点赞
收藏
分享

微信扫一扫

【C语言】高精度小数的乘法

//高精度小数的乘法 
#include<stdio.h> 
#include<string.h>

#define N 1000

//a[0]:数字长度len,a[M-1]:符号位,a[M-2]:整数位数
//数字从高位到低位,存储在len..1 
//a对应的数组长度可能不同,但符号位一定在最高位
//M:a对应数组的长度 
void output(int a[],int M){
    int i,j,k=0;        

    if(a[M-1]<0)
        printf("-");

    //处理小数点之后的后继0,如123.45000000, 0.000000
    for(i=1;i<a[0]-a[M-2];i++){
        if(a[i]==0){
            for(j=i+1;j<=a[0];j++)
                a[j-1]=a[j];
            a[0]--;
            i--; //防止移动过来的也是0 
        }
        else
            break;
    }   

    //处理因计算出现的前导0,前面处理的有bug,未同时减去数字长度
    //可以不用标志,注意条件是i>1 
    for(i=a[0];i>1;i--){
        if(a[i]==0&&a[M-2]>1){
            a[0]--;
            a[M-2]--;
        }       
        else
            break;
    }   

    for(i=a[0];i>=1;i--){

        //k记录已经输出的位数
        //整数位数至少为1,因此第一次循环一定不会输出. 
        if(k==a[M-2]) 
            printf(".");
        printf("%d",a[i]);
        k++;        
    }
    printf("\n");
}

//判断字符串是否为数字串,允许符号位和小数点,是返回1否返回0 
int allDigit(char *s) {
    int flag=1;//标志读取的是否第一个字符 
    int pointFlag=1; //标志读取到第一个小数点 
    while(*s) {
        //如果是第一个字符,允许是 + - 
        if(flag) {
            flag=0; 
            if(*s=='+'||*s=='-'){
                s++;
                continue;
            }                               
        }

        //不是数字字符且不是第一个小数点,return 0; 
        if(!(*s>='0'&&*s<='9')){
            //允许一个小数点 
            if(*s=='.'&&pointFlag==1) {
                pointFlag=0;                
            }
            else
                return 0;
        } 

        s++;
    }

    return 1;
}

//将数字串s拆分为单个数字保存在整型数组,从1号开始 
//整型数组0号元素记录数字长度 
//存储时去除所有的前导0 
//运行形如:+.12345的输入
void input(char s[],int p[]){
    int sign=1;//符号位 
    int len=strlen(s),i=0,j,k;

    //len表示s中数字字符的个数 
    //检测s中第一个是否是符号 
    if(s[0]=='+') {
        i=1;
        len--;          
    }
    if(s[0]=='-'){
        i=1;
        len--;
        sign=-1;
    }       

    //处理小数点在第一位或符号位之后的情况
    //如:+.12345或.12345 
    if(s[i]=='.'){
        //从\0开始到i号逐个后移 
        for(j=len+1;j>=i;j--)
            s[j+1]=s[j];
        //在.前补0 
        s[i]='0';
        len++;
    } 

    j=i;

    while(s[j]&&s[j]!='.')
        j++;
    if(s[j]){
        //有小数点 
        len--;   
    }   
    p[N-2]=j-i; //整数位数

    //跳过符号位之后所有的前导0
    //无+号时,从0号开始检测 
    // p[N-2]>1 限制小数点前要保留一个0,若输入000.1234 
    while(s[i]=='0'&&len>1&&p[N-2]>1){
        i++;
        len--;
        p[N-2]--; 
    }

    //i为s中除符号位第一个非0字符的下标 
    //若s中全0,i为最后一个0的下标 
    j=0;
    //注意条件是j<len,不能是i<len 
    for(;j<len;j++){
        //若转换时遇到小数点,不转换 
        if(s[i]=='.')
            j--;         
        else
            p[len-j]=s[i]-'0';
        i++;        
    }

    //0位记录数字长度,1..len记录数字
    //N-1位记录符号1或-1
    //考虑运算时两个数可能不等长,符号位不能设置在len+1   
    p[0]=len;
    p[N-1]=sign;
} 

// c=a*b a,b容量为N,c容量为2N 
void multiply(int a[],int b[],int c[]) {
    int i,j,k;
    int len1=a[0]-a[N-2]; //a中小数位数 
    int len2=b[0]-b[N-2]; //b中小数位数
    //a,b的符号位 
    int signa=a[N-1];      
    int signb=b[N-1];    

    c[0]=a[0]+b[0]; //积的长度 
    for(j=1;j<=b[0];j++){
        for(i=1;i<=a[0];i++)            
            c[i+j-1]+=a[i]*b[j]; //+=

        //先乘完,再统一处理进位 
        for(k=1;k<=c[0];k++)
            if(c[k]>=10){
                c[k+1]+=c[k]/10; //+=
                c[k]=c[k]%10;
            }               
    }

    //结果可能是m+n-1位 
    if(c[c[0]]==0) 
        c[0]--;

    //处理积的符号
    if(signa>0&&signb>0||signa<0&&signb<0) 
        c[2*N-1]=1;
    else
        c[2*N-1]=-1;

    //积的整数位数 
    c[2*N-2]=c[0]-len1-len2;

}

int main(){
    char s[N];
    int a[N]={0},b[N]={0},c[2*N]={0};
    int i,sign;

    printf("请输入数字串1:\n");
    do{
        gets(s);
        if(allDigit(s))
            break;
        printf("请输入数字串1:\n");
    }while(1);  
    input(s,a); 

    printf("请输入数字串2:\n");
    do{
        gets(s);
        if(allDigit(s))
            break;
        printf("请输入数字串2:\n");
    }while(1);      
    input(s,b);     

    multiply(a,b,c);

    output(c,2*N);  

    return 0; 
}
举报

相关推荐

0 条评论