大整数算法[08] 有符号加法和减法

★ 引子

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

★ 有符号数加法

           有符号数的加法分成两种情况:同号和异号。

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

时间: 2024-08-09 22:02:57

大整数算法[08] 有符号加法和减法的相关文章

大整数算法[08] Comba乘法(原理)

★ 引子          原本打算一篇文章讲完,后来发现篇幅会很大,所以拆成两部分,先讲原理,再讲实现.实现的话相对复杂,要用到内联汇编,要考虑不同平台等等. 在大整数计算中,乘法是非常重要的,因为在公钥密码学中模幂运算要频繁使用乘法,所以乘法的性能会直接影响到模幂运算的效率.下面将会介绍两种乘法:基线乘法和 Comba 乘法,尽管他们的原理和计算看起来十分类似,而且算法的时间复杂度都是 O(n^2),但是他们的效率差别是很大的. ★ 基线乘法 (Baseline Multiplication

基于Java的大整数运算的实现(加法,减法,乘法)学习笔记

大整数,顾名思义就是特别大的整数. 一台64位的机器最大能表示的数字是2的64次方减一: 18446744073709551615 java语言中所能表示的整数(int)最小为-2147483648 public class test { public static void main(String[] args) { System.out.println(Integer.MIN_VALUE); } } 最大为 2147483647 public class test { public stat

大整数算法[12] 有符号乘法

★ 引子 前面三篇文章讲了 Comba 乘法和 Karatsuba 乘法,有了这两个算法,就可以很轻松的构造有符号数乘法. 顺便提一下:讲Comba 乘法的实现的时候,给出了 x86 环境下的内联汇编实现,最近添加了 GCC x64 环境的内联汇编,已经补充到帖子当中. ★ 实现         有符号数的乘法,基本实现是这样:大的整数用 Karatsuba 乘法搞定,小的整数用 Comba 乘法搞定.对于大的整数,Karatsuba 乘法会不断递归计算,直到输入的整数小到一定规模,就改用 Co

大整数算法[00] 概述

★ 为啥要做这个 早在大一的时候,我便对密码学产生兴趣.那时在计算机导论后面看到RSA加密的计算原理,觉得十分有趣,于是就很想自己实现一个RSA加密,不过我很快就放弃了,因为实在搞不定那超长的整数计算.C里面最长的整数类型也就64位,对于动辄就1024位的RSA密钥,这连个零头都没有.为了完成这个目标,我便开始琢磨着弄一个用来计算大整数的库.原本我也打算使用别人已经写好的大数库,不过最终还是决定自己搞一个,因为凡是效率高速度快的大数库 (OpenSSL, Crypto++, GMP),要么使用的

大整数算法[01] 大整数的表示和相关定义

★ 相关的数据类型定义 在干正事之前,先定义好各种数据类型还是很有必要的,避免在以后的编码中引起混乱. uintX   X位无符号整形,如uint32表示32位无符号整形 intX    X位有符号整形,如int32表示32位有符号整形 基本数据类型定义: #ifdef _MSC_VER            typedef __int8              int8;            typedef __int16             int16;            typ

大整数算法[11] Karatsuba乘法

★ 引子         前面两篇介绍了 Comba 乘法,最后提到当输入的规模很大时,所需的计算时间会急剧增长,因为 Comba 乘法的时间复杂度仍然是 O(n^2).想要打破乘法中 O(n^2) 的限制,需要从一个完全不同的角度来看待乘法.在下面的乘法算法中,需要使用 x 和 y 这两个大整数的多项式基表达式 f(x) 和 g(x) 来表示. 令 f(x) = a * x + b,g(x) = c * x + d,h(x) = f(x) * g(x).这里的 x 相当于一个基,比如十进制下,

[转]大整数算法[11] Karatsuba乘法

★ 引子         前面两篇介绍了 Comba 乘法,最后提到当输入的规模很大时,所需的计算时间会急剧增长,因为 Comba 乘法的时间复杂度仍然是 O(n^2).想要打破乘法中 O(n^2) 的限制,需要从一个完全不同的角度来看待乘法.在下面的乘法算法中,需要使用 x 和 y 这两个大整数的多项式基表达式 f(x) 和 g(x) 来表示. 令 f(x) = a * x + b,g(x) = c * x + d,h(x) = f(x) * g(x).这里的 x 相当于一个基,比如十进制下,

大整数算法[13] 单数位乘法

★ 引子 最近在折腾 wxWidgets,同时拖延症又犯了,所以中断了好久.这次来讲讲单数位乘法,前面讲到 Comba 和 Karatsuba 乘法,这两个算法适合用来处理比较大的整数,但是对于一个大整数和一个单精度数相乘,其效果反而会不好,因为计算量过多.实际上单数位乘法只是基线乘法的一个特例,不存在嵌套循环进位,因此可以通过优化减少计算量.另外与完整的乘法不同的是,单数位乘法不需要什么临时变量存储和内存分配(目标精度增加除外). ★ 算法思路         单数位乘法类似于计算 12345

大整数算法

本文主要整理了几个常用的大整数的算法:大整数加法大整数乘法大整数阶乘大整数幂其实大体的思路都差不多,都是用数组来存储大整数.以下的代码仅仅实现功能,并没有充分详细的参数判断,在实际运用中,肯定是需要考虑的. 大整数相加 1 #include <stdio.h> 2 #include <string.h> 3 #define N 1000 4 void get_num(int *array) 5 { 6 char str[N] = {'\0'}; 7 int loop = 0; 8