★ 引子

           前面几篇文章介绍了比较操作绝对值加法绝对值减法,现在就可以利用这几个算法构建有符号数的加减算法。

        ★ 有符号数加法
           有符号数的加法分成两种情况:同号和异号。
           1.  如果两个数同号,则执行绝对值加法,如果两个数为非负数,则结果为非负数;如果两个数都是负数,则结果也为负数。
           2.  如果两个数异号,则要执行绝对值减法,用绝对值较大的数去减绝对值较小的数。最终结果 z 的符号由 x 和 y 的绝对值大小决定:如果 x 的绝对值大于或等于 y,则 z 的符号与 x 相同(注意这里可能出现 -0 的情况),否则 z 的符号与 x 相反。
int bn_add_bn(bignum *z, const bignum *x, const bignum *y)
{
    int ret;
    int sign;

    sign = x->sign;

    if(x->sign == y->sign)
    {
        BN_CHECK(bn_add_abs(z, x, y));
        z->sign = sign;
    }
    else
    {
        if(bn_cmp_abs(x, y) >= 0)
        {
            BN_CHECK(bn_sub_abs(z, x, y));
            z->sign = sign;
        }
        else
        {
            BN_CHECK(bn_sub_abs(z, y, x));
            z->sign = -sign;
        }
    }

    if(BN_IS_ZERO(z))
        z->sign = 1;

clean:

    return ret;
}

        要注意的是,如果两个数异号,但绝对值相等,则可能出现 -0 的现象(例如 x =  -a, y = a),这与之前的规定不符,所以在最后加一句判断,如果 z = 0,强制把符号位设为 1。BN_IS_ZERO 是一个宏定义:

        #define BN_IS_ZERO(x)                 ((x->used == 0) ? 1 : 0)

        ★ 有符号数减法

           和有符号数的加法类似,有符号数减法也分成两种情况:

           1.  两个数异号:执行绝对值加法。结果 z 的符号由 x 决定,如果 x 为非负数,则 z 为正数;如果 x 为负数,则 z 为负数。

           2.  两个数同号:执行绝对值减法,用绝对值较大的数去减绝对值较小的数。结果 z 的符号由 x 和 y 的绝对值大小决定,如果 x 的绝对值大于或等于 y 的绝对值,则 z 和 x 同号(可能出现 -0),否则 z 与 x 异号。

int bn_sub_bn(bignum *z, const bignum *x, const bignum *y)
{
    int ret;
    int sign;

    sign = x->sign;

    if(x->sign != y->sign)
    {
        BN_CHECK(bn_add_abs(z, x, y));
        z->sign = sign;
    }
    else
    {
        if(bn_cmp_abs(x, y) >= 0)
        {
            BN_CHECK(bn_sub_abs(z, x, y));
            z->sign = sign;
        }
        else
        {
            BN_CHECK(bn_sub_abs(z, y, x));
            z->sign = -sign;
        }
    }

    if(BN_IS_ZERO(z))
        z->sign = 1;

clean:

    return ret;
}

        同样,为了避免出现 -0 的情况,在末尾添加一个对 z 是否等于 0 的判断。

        ★ 单数位加法和减法

           单数位算法,主要是计算一个大整数和一个单精度数的计算。这两个算法在处理小规模数据的加减会很有用。对于单数位加法和减法,默认输入是一个大整数和一个有符号的单精度数,结果为一个大整数。在处理上,并不是重新编写两个算法,而是先将单精度数赋值给一个临时的 bignum 变量,然后利用上面的两个有符号数算法进行计算。

           1.  单数位加法:

int bn_add_int(bignum *z, const bignum *x, const bn_sint y)
{
    int ret;
    bignum t[1];
    bn_digit p[1];

    p[0] = (y >= 0) ? y : -y;
    t->used = (y != 0) ? 1 : 0;
    t->sign = (y >= 0) ? 1 : -1;
    t->dp = p;
    t->alloc = 1;

    BN_CHECK(bn_add_bn(z, x, t));

clean:

    return ret;
}

             2.  单数位减法:

int bn_sub_int(bignum *z, const bignum *x, const bn_sint y)
{
    int ret;
    bignum t[1];
    bn_digit p[1];

    p[0] = (y >= 0) ? y : -y;
    t->used = (y != 0) ? 1 : 0;
    t->sign = (y >= 0) ? 1 : -1;
    t->dp = p;
    t->alloc = 1;

    BN_CHECK(bn_sub_bn(z, x, t));

clean:

    return ret;
}

        ★ 总结

           到此位置,大整数的加减算法就讲完了,实现加减法的关键还是分类的思想,这样就可以把复杂的问题简单化,然后各个击破。后面几篇将会着重介绍乘法的计算,乘法要比加减法复杂,而且在计算中,乘法是比较耗时间的,所以要做很多优化工作,否则后面的幂乘将会十分耗时。

回到本系列目录】 

版权声明
原创博文,转载必须包含本声明,保持本文完整,并以超链接形式注明作者Starrybird和本文原始地址:http://www.cnblogs.com/starrybird/p/4401863.html