★ 引子
前面几篇文章介绍了比较操作,绝对值加法和绝对值减法,现在就可以利用这几个算法构建有符号数的加减算法。
★ 有符号数加法
有符号数的加法分成两种情况:同号和异号。
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