• 大整数算法[07] 绝对值减法


            ★ 引子 

            最近两个星期一直在折腾,主机从 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

  • 相关阅读:
    用vuex写了一个购物车H5页面的示例代码
    css如何引入外部字体?
    移动开发中更好的图片自适应
    常见样式问题七、word-break、word-wrap、white-space区别
    你真的了解word-wrap和word-break的区别吗?
    css中word-break、word-wrap和white-space的区别
    另辟蹊径:vue单页面,多路由,前进刷新,后退不刷新
    应该用forEach改变数组的值吗? 原生JS forEach()和map()遍历的异同点
    Vue 全家桶介绍
    Spring MVC配置MyBatis输出SQL
  • 原文地址:https://www.cnblogs.com/starrybird/p/4399652.html
Copyright © 2020-2023  润新知