高精度加减乘除

来源:互联网 发布:seo最基础的代码 编辑:程序博客网 时间:2024/05/21 21:47

一。高精度乘法(敲了好久。。头秃)

//str1,str2是存数字的字符串,返回最后的位数,c存储结果int highmult( char str1[], char str2[], int c[] ){    int len1 = strlen(str1), len2 = strlen(str2);    int a[len1 + 1] = {0}, b[len2 + 1] = {0};    int i, j;    memset(a, 0, sizeof(a));    memset(b, 0, sizeof(b));    memset(c, 0, sizeof(c));
/*
*用这个函数写1000阶乘时这句话没用了,
*不知道为啥,可把我无奈坏了,找了半天错,
*好像memset对int数组不好用,有时候出问题
*/
    //把字符变成数字,方便运算    for( i = len1 - 1, j = 0; i >= 0; i--)        a[j++] = str1[i] - '0';    for( i = len2 - 1, j = 0; i >= 0; i--)        b[j++] = str2[i] - '0';    for( i = 0; i < len1; i++)//i, j是权重,i+j就是c[]里面的权重        for( j = 0; j < len2; j++)        {            c[i + j] += a[i] * b[j];            printf("%d\n", c[i + j]);            if( c[i + j] >= 10 )//因为9 + 9 * 9 < 100,所以只进一位            {                int k = c[i + j];                c[i + j + 1] += k / 10;                c[i + j] = k % 10;            }        }    
    //找位数。。。这个大概可以在前面的代码里优化,,懒,,    for( i = len1 + len2 + 2; (c[i] == 0) && (i >= 0); i-- )        ;    return i + 1;}

二。高精度加法

这个是我还不知道高精度是啥东西时百度的算法,记不得找的哪儿的了,一直存着,稍微改了点,,,原创看见了误喷一定加上链接。

恩!原创棒棒的!

//加起来存进c
int highadd( char num1[], char num2[], int c[] ){    int lena = strlen(num1), lenb = strlen(num2), lenc, i, x;//x 是进位用的    int a[100], b[100];    memset(a, 0, sizeof(a));    memset(b, 0, sizeof(b));    memset(c, 0, sizeof(c));
    //存数字    for( i = 0; i < lena; i++)        a[lena - i - 1] = num1[i] - '0';    for( i = 0; i < lenb; i++)        b[lenb - i - 1] = num2[i] - '0';    lenc = 0;    x = 0;    while( lenc <= lena || lenc <= lenb )    {        c[lenc] = a[lenc] + b[lenc] + x;        x = c[lenc] / 10;        c[lenc] %= 10;        lenc++;    }    c[lenc] = x;    if( c[lenc] == 0 )        lenc--;    return lenc;}

三。高精度减法

//贼他妈难写,
int highsub( char a[], char b[], int c[] )
{    for(int i = 0; i < MAXLEN; ++i)        c[i] = 0;    int la = strlen(a), lb = strlen(b);    int sign(0), m(0);    int com = compare(a, b);
    
    //记录符号    if( com > 0 )    {        sign = 1;        m = la;    }    else if( com < 0)    {        sign = -1;        m = lb;    }    else        return 1;//一位以便于输出0    if(sign == 1)    {        int sub = la - lb;        for(int i = 0; i < sub; ++i)//先把前面的权重高的解决了            c[i] = a[i] - '0';        for(int i = sub; i < m; ++i)//和我写的乘法一样,算一次就处理一次进位        {            if( a[i] > b[i - sub])                c[i] = a[i] - b[i - sub];            else if( a[i] == b[i - sub] )                ;            else//小于时向上一位要一个,后面再集中处理如果上一位也是零的情况            {                --c[i - 1];                c[i] = (a[i] - '0') + 10 - (b[i - sub] - '0');            }        }    }    else//一样的    {        int sub = lb - la;        for(int i = 0; i < sub; ++i)            c[i] = b[i] - '0';        for(int i = sub; i < m; ++i)        {            if( b[i] > a[i - sub])                c[i] = b[i] - a[i - sub];            else if( a[i - sub] == b[i])                ;            else            {                --c[i - 1];                c[i] = (b[i] - '0') + 10 - (a[i - sub] - '0');            }        }    }    for(int i = m - 1; i >= 0; --i)//处理退位时产生的负数问题        if( c[i] < 0 )        {            --c[i - 1];            c[i] += 10;        }    if(c[0] == 0)//处理a, b中前几位相同以至于c前几位是0的问题        for(int i = 1; i < MAXLEN; ++i)            if( c[i] != 0 )            {                for(int j = i; j < m; ++j)                    c[j - i] = c[j];                m -= i;                break;            }    c[0] *= sign;    return m;}

四。高精度除法

这个代码对于大数不能算位数相差超过六位的,太慢了,因为是用减法实现的,循环减,减了几次答案就是几

以后灵光一闪优化了(偷偷百度一下)再修改吧

(最难写的高精度。。)

//要存小数点就用字符数组c了,n是确定小数点后几位,默认c里面都是0,懒得加memset
int highdiv( char a[], char b[], char c[], int n ){    int d[MAXLEN];    int j(0), k(0);    int zs;//记录小数点位置,便于小数部分运算
    
    //代码核心,把除降解为减    while( compare( a, b ) >= 0 )    {        int len = highsub(a, b, d);        memset(a, 0, sizeof(0));        for(int i = 0; i < len; ++i)            a[i] = d[i] + '0';        a[len] = '\0';        ++c[j];        if(c[j] >= 10)//烦人的进位问题        {            for(int i = j; i > 0; --i)                if( c[i] >= 10 )                {                    ++c[i - 1];                    c[i] %= 10;                }            if(c[0] >= 10)//进到头了就把c整体往后挪一位            {                for(int i = j; i >= 0; --i)                    c[i + 1] = c[i];                c[0] = 1;                c[1] = 0;                ++j;            }        }    }    zs = j + 2;//实际上是指向了小数点后一位
    //把减的比b小的a膨胀开来,添加n + 1个0,模拟手算的加0过程    for(int i = strlen(a); i < MAXLEN && k <= n; ++i, ++k)    {        a[i] = '0';        if( k == n )            a[i + 1] = '\0';    }    j += 2;//j指向第一个小数位,再来一波和上面差不多的操作    while( compare( a, b ) >= 0 )    {        int len = highsub(a, b, d);        memset(a, 0, sizeof(0));        for(int i = 0; i < len; ++i)            a[i] = d[i] + '0';        a[len] = '\0';        ++c[j];        if(c[j] >= 10)        {            for(int i = j; i > zs; --i)                if( c[i] >= 10 )                {                    ++c[i - 1];                    c[i] %= 10;                }            if(c[zs] >= 10)            {                for(int i = j; i >= zs; --i)                    c[i + 1] = c[i];                c[zs] = 1;                c[zs + 1] = 0;                ++j;            }        }    }    k = j;    if( c[k] >= 5 )//四舍五入,循环是怕一入就出现了10        do        {            ++c[k - 1];            c[k] = 0;        }        while( c[--k] >= 10 );    j = zs + n;    for(int i = 0; i <= j; ++i)//数字变字符,便于小数点打印        c[i] += '0';    c[zs - 1] = '.';    c[j] = '\0';    return j + 1;}

最后把compare也贴出来吧,不复杂

int compare(char a[], char b[]){    int la = strlen(a), lb = strlen(b);    if( la > lb )        return 1;    else if( la < lb )        return -1;    else        for(int i = 0; i < la; ++i)            if( a[i] > b[i] )                return 1;            else if( a[i] < b[i] )                return -1;    return 0;}

还是初学者,自己敲可能有bug,也应该还可以优化,比如处理进位写成函数什么的。

要是以后变大佬了就回来优化一波算法。




原创粉丝点击