★ 引子 

        最近两个星期一直在折腾,主机从 Windows 换到了 Linux,工作环境从实体机转移到虚拟机中。当然目的只有一个,那就是充分发挥 Linux 和虚拟机的优势来提高自己的工作效率。俗话说得好:磨刀不误砍柴工,花费一些时间来折腾升级还是有必要的,有空的话可以聊聊相关经验,如果你想急于知道的话,推荐编程随想的博客(博客在墙外,请自行搜索梯子)。

 

        ★ 计算原理

         好了,废话不多说。上一篇文章讲了绝对值加法的实现,这次来讲讲绝对值减法该如何做。绝对值减法的做法仍然是笔算算法,从低位开始减,不够的向高位借位,直到所有的数位都处理完毕。为了方便日后的有符号数加减的实现,这里规定,算法计算 z = x - y,并且 x 的绝对值大于或等于 y,否则算法返回负数错误。

 

         ★ 实现

          因为原理比较简单,所以我就先把代码贴出来,然后在介绍他的工作方式。

int bn_sub_abs(bignum *z, const bignum *x, const bignum *y)
{
    int ret;
    bn_digit *px, *py, *pz;
    size_t i, min, max, olduse, t1, t2, c;

    max = x->used;
    min = y->used;

    if(bn_cmp_abs(x, y) < 0)
        return BN_NEGATIVE_VALUE_ERROR;

    olduse = z->used;
    z->used = max;
    BN_CHECK(bn_grow(z, z->used));

    c = 0;
    px = x->dp;
    py = y->dp;
    pz = z->dp;

    for(i = 0; i < min; i++)
    {
        t1 = *px++;
        t2 = *py++;
        *pz++ = t1 - t2 - c;
        if(t1 != t2) c = (t1 < t2);
    }

    for(; i < max; i++)
    {
        t1 = *px++;
        *pz++ = t1 - c;
        if(c != 0 && t1 != 0) c = 0;
    }

    for(i = max; i < olduse; i++)
        *pz++ = 0;

    z->sign = 1;
    bn_clamp(z);

clean:

    return ret;
}

           绝对值减法中,对输入进行排序并不重要,因为前面已经规定 |x| >= |y|,所以直接把 x->used 给 max, y->used 给 min;t1 和 t2 是临时变量,c 是借位。

           在进行计算之前,先检查 x 和 y 的绝对值大小,如果不满足上面约定的条件,返回负数错误。

           如果 x 和 y 的绝对值大小检查没问题,那么计算就可以正常进行,首先把借位的值设为 0,然后设定指针别名来提高内存访问效率。

           第一个循环:对位相减。分别把 x 和 y 的每一个数位赋值给临时变量 t1 和 t2,计算 t1 - t2 - c 的值,然后存放到 z 的对应数位当中,如果 c = 0,表示低位没有向高位借位。相减完毕后,判断本次相减是否需要向高位借位,如果原来 x 中的某一数位的值小于 y 中对应数位的值,则比较的结果为 1,c = 1。注意所有的计算都是 mod 2^n。

           第二个循环:退位和赋值。如果 max > min,表明 x 的数位要比 y 多,所以还需进行退位计算。如果 c = 0,则不会有退位了,直接把 x 的剩余数位赋值给 z 的对应数位即可。如果 c = 1,则还有来自低位的借位,在完成一次退位计算后,判断下一位是否需要退位,由于 c 的值只可能是 0 或 1,如果本次退位计算前,该数位的值大于 0,则以后的数位都不需要进行退位,故将 c 的值置为 0,否则保持退位值 1。完成退位计算后,将 x 剩余的数位给 z ,完成减法计算。

           第三个循环:高位清零。如果减法计算完毕后,高位还有不为 0 的数位,必须清空,否则结果会出错。

           所有循环结束后,把符号为设为 1,因为绝对值减法的最终结果仍然是个非负整数;最后压缩多余位完成计算。

 

         ★ 总结

             减法操作相对于加法来讲要简单些,主要是不需要考虑单双精度的问题,只要你知道笔算算法以及理解计算机下二进制的补码运算,就不难实现。下一篇文章将根据前面建立的比较算法,绝对值加减算法构造有符号数的加减计算算法。

 

 

   【回到本系列目录】 

 

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