带符号大整数加减法,乘法,除法,模除,指数
2012-11-21 11:50
281 查看
一、问题的思考
1、对于大整数,我们只需要提供构造,显示,加减法,乘法,除法,模除,指数这些用户接口就可以了。2、符号位怎么解决?首先考虑的办法是定义一个类,然后里面有sign_, std::vector<char> i_这两个成员变量,我在实现的过程中发现这样写代码,思路不够清晰,代码也不好写。因为当我们编写代码实现加减乘除,比较运算的时候,就不得不考虑符号位,这种设计会使代码的逻辑复杂,不够清晰。
3、正确的办法是先编写一个UnsignedInteger类实现所有的运算,然后再实现有符号的大整数运算。
4、如何实现无符号大整数加减乘除(减法只支持大减小)。加减都很简单,乘法只需提供基本UnsignedInteger * (0--9),UnsignedInteger * (10 ^K),然后利用加法运算,就可以实现乘法。除法则需要利用UnsignedInteger
* (0--9),UnsignedInteger * (10 ^K),比较运算,减法运算,就可以实现除法。
5、无符号大整数的存储采用std::vector<char>,每一个char表示一个十进制位,并且第一个char表示最低位,最后一个char表示最高位
1、无符号整数的基础运算
代码如下:#include <iostream> #include <vector> #include <assert.h> #include <cmath> #include <utility> class UnsignBigInteger { private: std::vector< char > i_; void compact() { int count = 0; for( int i = i_.size() - 1; i >= 0; i-- ) if( i_[i] == 0 ) count++; else break; //resize dose not change storage capacity directly i_.resize( i_.size() - count ); } void expand10( int k ) { int curSize = i_.size(); int expandSize = curSize + k; i_.resize( expandSize, 0 ); copy_backward( i_.begin(), i_.begin() + curSize, i_.end() ); fill_n( i_.begin(), k, 0 ); } void add( const UnsignBigInteger &other ) { int max = std::max( i_.size(), other.i_.size() ); //to reserve one more place, so it is enough to hold sum i_.resize( max + 1, 0 ); int c = 0; for( unsigned i = 0; i < i_.size(); i++ ) { int value = i_[i] + c ; if( i < other.i_.size() ) value += other.i_[i]; i_[i] = value % 10; c = value / 10; } } void sub( const UnsignBigInteger &o ) { if( o.bigThan(*this) ) { std::cout << "the other must small then current" << std::endl; return ; } int b = 0; for( unsigned i = 0; i < i_.size(); i++ ) { i_[i] -= b; if( i < o.i_.size() ) i_[i] -= o.i_[i]; if( i_[i] < 0 ) { i_[i] += 10; b = 1; } else { b = 0; } } compact(); } void mul( int m ) { assert( m >= 0 && m < 10 ); i_.resize( i_.size() + 1, 0 ); int c = 0; for( unsigned i = 0; i < i_.size(); i++ ) { int value = i_[i] * m + c; i_[i] = value % 10; c = value / 10; } } const char & operator[]( int i ) const { return i_[i]; } int size() const { return i_.size(); } public: bool bigThan( const UnsignBigInteger &o ) const { if( i_.size() != o.i_.size() ) return i_.size() > o.i_.size(); for( unsigned i = i_.size() - 1; i >= 0; i-- ) if( i_[i] != o.i_[i] ) return i_[i] > o.i_[i]; return false; } void print(std::ostream &out) const { for( int i = i_.size() - 1; i >= 0; i-- ) out << static_cast< int >( i_[i] ); } //we must stop the implicit conversion explicit UnsignBigInteger( const std::string & nums ) : i_(nums.size(), 0){ for( unsigned i = 0; i < nums.size(); i++ ) i_[i_.size() - 1 - i] = nums[i] - '0'; } explicit UnsignBigInteger( int n ) : i_(0){ int v = n; while( v != 0 ) { i_.push_back( v % 10 ); v /= 10; } } UnsignBigInteger() : i_(0) { } friend UnsignBigInteger& operator+=( UnsignBigInteger &lhs, const UnsignBigInteger &rhs ); friend UnsignBigInteger& operator-=( UnsignBigInteger &lhs, const UnsignBigInteger &rhs ); friend UnsignBigInteger operator-( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ); friend UnsignBigInteger operator+( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ); friend UnsignBigInteger operator*( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ); friend std::ostream & operator<<( std::ostream &out, const UnsignBigInteger &b ); friend UnsignBigInteger power( const UnsignBigInteger &b, int e ); friend std::pair<UnsignBigInteger, UnsignBigInteger> div( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs); friend UnsignBigInteger operator/( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ); friend UnsignBigInteger mod( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ); };该算法有一个非常重要的技巧是加法多预留一个字符,计算的时候就不会溢出,所有计算完compact, 这样就不会出问题
2、无符号整数运算的实现
UnsignBigInteger实现了大整数的基本运算,然后将需要的大整数运算定义为友元,下面是这些运算的实现UnsignBigInteger& operator+=( UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) { lhs.add( rhs ); lhs.compact(); return lhs; } UnsignBigInteger operator+( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) { UnsignBigInteger tmp; tmp += lhs; tmp += rhs; tmp.compact(); return tmp; } UnsignBigInteger& operator-=( UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) { lhs.sub( rhs ); return lhs; } UnsignBigInteger operator-( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) { UnsignBigInteger ret( lhs ); ret.sub( rhs ); return ret; } UnsignBigInteger operator*( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) { UnsignBigInteger ret; for( int i = 0; i < rhs.size(); i++ ) { UnsignBigInteger tmp( lhs ); tmp.mul( rhs[i] ); tmp.expand10( i ); ret += tmp; } ret.compact(); return ret; } std::pair<UnsignBigInteger, UnsignBigInteger> div( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs) { UnsignBigInteger u1(lhs), u2(rhs), ret; while( !u2.bigThan(u1) ) { unsigned s1 = u1.size(), s2 = u2.size(); unsigned diff = s1 - s2; UnsignBigInteger temp(u2); temp.expand10(diff); if( temp.bigThan(u1) ) diff--; UnsignBigInteger temp2(u2); temp2.expand10(diff); //i use a very naive way to calculate the most significant of the result int count = 0; while( !temp2.bigThan(u1) ) { u1 -= temp2; 4000 count++; } //accumulate the result UnsignBigInteger cur( count ); cur.expand10( diff ); ret += cur; u1.compact(); } return std::pair<UnsignBigInteger, UnsignBigInteger>(ret, u1); } UnsignBigInteger operator/( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) { return div( lhs, rhs ).first; } std::ostream & operator<<( std::ostream &out, const UnsignBigInteger &b ) { b.print( out ); return out; } UnsignBigInteger mod( const UnsignBigInteger &lhs, const UnsignBigInteger &rhs ) { return div( lhs, rhs ).second; } //tail recursion, how to eliminate? UnsignBigInteger power( const UnsignBigInteger &b, int e ) { if( e == 0 ) return UnsignBigInteger( 1 ); else if( (e & 1) == 1 ) return b * power( b, e - 1 ); else { UnsignBigInteger tmp = power( b, e / 2 ); return tmp * tmp; } }
3、有符号整数运算的实现
因为无符号的和有符号的大整数之间不是继承的关系,我倾向于组合,因此将无符号的大整数作为有符号的一个成员。然后需要做的就是基于无符号的大整数运算实现有符号的。利用运算的基本规则,有符号的加:如果同号,符号位为两个数的共同符号,如果不同号,则利用无符号的较大减去较小,然后符号位和较大的相同,其他运算和小学的运算规则一样。
class SignBigInteger { private: bool sign_; UnsignBigInteger unbi_; public: SignBigInteger( const std::string &s, bool p = true) : sign_(p), unbi_(s) { }; SignBigInteger(): sign_(true) { } friend SignBigInteger operator-( const SignBigInteger &s ); friend SignBigInteger& operator+=( SignBigInteger &lhs, const SignBigInteger &rhs ); friend SignBigInteger& operator-=( SignBigInteger &lhs, const SignBigInteger &rhs ); friend SignBigInteger operator-( const SignBigInteger &lhs, const SignBigInteger &rhs ); friend SignBigInteger operator+( const SignBigInteger &lhs, const SignBigInteger &rhs ); friend SignBigInteger operator*( const SignBigInteger &lhs, const SignBigInteger &rhs ); friend std::ostream & operator<<( std::ostream &out, const SignBigInteger &b ); friend SignBigInteger power( const SignBigInteger &b, int e ); friend std::pair<SignBigInteger, SignBigInteger> div( const SignBigInteger &lhs, const SignBigInteger &rhs); friend SignBigInteger operator/( const SignBigInteger &lhs, const SignBigInteger &rhs ); friend SignBigInteger mod( const SignBigInteger &lhs, const SignBigInteger &rhs ); }; SignBigInteger operator-( const SignBigInteger &s ) { SignBigInteger temp( s ); temp.sign_ = !temp.sign_; return temp; } SignBigInteger& operator+=( SignBigInteger &lhs, const SignBigInteger &rhs ) { if( lhs.sign_ == rhs.sign_ ) { lhs.unbi_ += rhs.unbi_; } else { if( lhs.unbi_.bigThan( rhs.unbi_ ) ) { lhs.unbi_ -= rhs.unbi_; } else { lhs.unbi_ = rhs.unbi_ - lhs.unbi_; lhs.sign_ = rhs.sign_; } } return lhs; } SignBigInteger& operator-=( SignBigInteger &lhs, const SignBigInteger &rhs ) { return lhs += (- rhs); } SignBigInteger operator-( const SignBigInteger &lhs, const SignBigInteger &rhs ) { SignBigInteger temp(lhs); return temp -= rhs; } SignBigInteger operator+( const SignBigInteger &lhs, const SignBigInteger &rhs ) { SignBigInteger temp( lhs ); return temp += rhs; } SignBigInteger operator*( const SignBigInteger &lhs, const SignBigInteger &rhs ) { SignBigInteger sb; if( lhs.sign_ == rhs.sign_ ) sb.sign_ = true; else sb.sign_ = false; sb.unbi_ = lhs.unbi_ * rhs.unbi_; return sb; } std::ostream & operator<<( std::ostream &out, const SignBigInteger &b ) { if( b.sign_ == false ) out << "-"; out << b.unbi_; return out; } SignBigInteger power( const SignBigInteger &b, int e ) { SignBigInteger sb; sb.sign_ = b.sign_; if( (b.sign_ == false) && ((e & 1) == 0) ) sb.sign_ = true; sb.unbi_ = power( b.unbi_, e ); return sb; }
当然,代码写到这里并没有结束,我们还需要实现无符号大整数和有符号大整数的相互转换,以及和其他一些数字的转换,不然支持的运算太有限了。
二、测试
最后需要做的就是测试,下面是测试代码:int main() { UnsignBigInteger b1("12312345678901023456"); UnsignBigInteger b2("4561233123123123123123"); UnsignBigInteger b3(123); UnsignBigInteger b4(321); std::cout << b1 + b2 << std::endl; std::cout << b1 * b2 << std::endl; std::cout << b3 << std::endl; std::cout << b1 * b3 << std::endl; std::cout << power( b3, 2 ) << std::endl; std::cout << power( b3, 1100 ) << std::endl; std::cout << b4 - b3 << std::endl; std::cout << b2 - b1 << std::endl; std::cout << b4 / b3 << std::endl; std::cout << b2 / b1 << std::endl; std::cout << mod( b2, b1 ) << std::endl; std::cout << mod( b4, b3 ) << std::endl; SignBigInteger sb1("12345", false), sb2("123"); std::cout << power(sb1, 20) << std::endl; std::cout << sb1 * sb2 << std::endl; return 0; }
下面是执行结果,因为lisp里面支持大整数,我在lisp里面也执行相同的运算,结果是一样的。
![](http://img.my.csdn.net/uploads/201211/29/1354193225_7785.png)
扩展
实现整数到大整数,大整数到整数的转换,这样可以丰富运算的数据类型采用std::vector<int>实现底层,速度至少可以提高8-9倍
相关文章推荐
- [算法]用位运算的方法实现无符号整数的除法原理及程序
- 有符号整数判断溢出的乘法
- 汇编总结:无符号除法,有符号除法,取余,无符号乘法,有符号乘法指令
- 使用main函数的参数,实现一个整数计算器,程序可以接受三个参数,第一个参数“-a”选项执行加法,“-s”选项执行减法,“-m”选项执行乘法,“-d”选项执行除法,后面两个参数为操作数。
- 大数 (整数)乘法,除法
- 判断两个有符号整数的加法和乘法是否溢出
- 使用main函数的参数,实现一个整数计算器,程序可以接受三个参数,第一个参数“-a”选项执行加法,“-s”选项执行减法,“-m”选项执行乘法,“-d”选项执行除法,后面两个参数为操作数。
- 汇编语言使用加减法实现有符号数除法
- 使用main函数的参数,实现一个整数计算器,程序可以接受三个参数,第一个参数“-a”选项执行加法,“-s”选项执行减法,“-m”选项执行乘法,“-d”选项执行除法,后面两个参数为操作数。
- 【Java】只允许使用加号,实现整数的减法,乘法,除法
- 实现整数的乘法,减法和除法运算。只允许使用加号
- 有符号整数除法指令 IDIV 汇编基础一日一学习33
- 大整数的加减乘法,没有除法,你想累死我啊?
- szuoj b47 有符号大整数加减法
- 判断一个整数是否是n^m次方类型数据,并比较乘法和除法性能差异
- 带符号整数的除法与余数
- 整数高精度运算的库(加法,减法,乘法,除法,取模)
- 实现一个整数计算器,程序可以接受三个参数,第一个参数“-a”选项执行加法,“-s”选项执行减法,“-m”选项执行乘法,“-d”选项执行除法,后面两个参数为操作数。 例如:输入t
- 有符号整数除法指令 IDIV(转帖)
- 实现整数的乘法、减法和除法运算,只允许使用加号