5.HugeNumberBase.h
————————————————————————————————————————
/*
* Copyright (c) 2005 by DoZerg. ALL RIGHTS RESERVED.
* Consult your license regarding permissions and restrictions.
*/
/************************Things to do***************************
1.Using Exception-Class to indicate whether to throw an exception or not when an error occurs
(DONE)2.Using Memory-Class to indicate Memory Allocation Strategy
(DONE)3.Type conversion with operator +,-,*,/,%
4.Using Traits to eliminate type specialization with unsigned long
5.Performance Profile,especially for operator << and >>
***************************************************************/
#pragma once
#include "Helper.h"
#include "DataAllocation.h"
namespace DoZerg{
//declaration.
template<
long N,
template<long,typename>class __Alloc
>class HugeNumberBase
{
__Alloc<N,unsigned long> data_; //the highest byte is data_[0],and the lowest is data_[N-1]
//unsigned long data_[N];
static const long EachBits; //bits per data_
static const long TotalBits; //total bits of data_[N]
public:
static const long TotalBytes; //total bytes of data_[N]
private:
static const unsigned long SignBit;
void AddOne(long); //add 1 in a particular position
bool EqualValue(unsigned long) const; //test if *this is equal to a value
long HighestBit() const; //return the highest bit position based on 0
long LowestBit() const; //return the lowest bit position based on 0
HugeNumberBase & ResetHigherBits(long); //reset the higher bits than a value to 0
HugeNumberBase & RightShift();
HugeNumberBase & LeftShift();
protected:
HugeNumberBase(){}
explicit HugeNumberBase(long);
explicit HugeNumberBase(const std::string &);
template<long M,class T>
explicit HugeNumberBase(const HugeNumberBase<M,T> &);
HugeNumberBase & FromLong(long);
HugeNumberBase & FromString(const std::string &);
template<long M,class T>
HugeNumberBase & FromSelf(const HugeNumberBase<M,T> &);
HugeNumberBase & Increase(); //operator ++()
HugeNumberBase & Decrease(); //operator --()
HugeNumberBase & Complement(); //self ~()
HugeNumberBase & Minus(); //self -()
HugeNumberBase & RightShift(long,bool = false); //the bool means whether to treat *this as a signed value
HugeNumberBase & LeftShift(long);
HugeNumberBase & OperatorAdd(const HugeNumberBase &);
HugeNumberBase & OperatorSub(const HugeNumberBase &);
HugeNumberBase & OperatorMult(const HugeNumberBase &);
HugeNumberBase & OperatorAnd(const HugeNumberBase &);
HugeNumberBase & OperatorOr(const HugeNumberBase &);
HugeNumberBase & OperatorXor(const HugeNumberBase &);
HugeNumberBase & OperatorDiv(const HugeNumberBase &,bool = true); //return divided result if bool==true by default,otherwise return the modulus
HugeNumberBase & OperatorPower(const HugeNumberBase &);
bool OperatorEqual(const HugeNumberBase & value) const;
bool OperatorSmaller(const HugeNumberBase & value) const;
bool OperatorLarger(const HugeNumberBase & value) const;
bool OperatorNotSmaller(const HugeNumberBase & value) const;
bool OperatorNotLarger(const HugeNumberBase & value) const;
const std::string ToString(int = 10,bool = false) const; //the bool means whether to treat *this as a signed value
public:
long ToLong() const;
bool GetSignBit() const;
const bool NonZero() const;
const bool operator !() const;
const void * DataPointer(long = 0) const; //return the n-th data_'s pointer
//temporary functions
void show() const{
for(long i=0;i<N;i++)
cout<<hex<<data_[i]<<' ';
cout<<dec<<"Highbit:"<<HighestBit()
<<" Lowbit:"<<LowestBit();
cout<<endl;
}
//friend functions. for several reasons they can't be implemented outside
friend std::istream & operator >>(std::istream & is,HugeNumberBase & value){
std::string tmp;
is>>tmp;
value.FromString(tmp);
return is;
}
};
template<class T>class HugeNumberBase<0,T>{};
......
————————————————————————————————————————
终于轮到HugeNumberBase了,其实我早就已经迫不及待了。但是不幸的是,当我看到HugeNumberBase.h的代码才知道,继续采用前面的“代码+解说”形式会有什么后果:要么就是这篇文章过长,要么就是读者不停的在代码页面和解说页面间切换。
所以我决定作一个变革,我先把HugeNumberBase的定义给出来,也就是HugeNumberBase.h的前半部分;然后在以后的文章里给出实现代码,并一一讲解。我想这样作者和读者都要方便很多。
HugeNumberBase的内部成员变量只有一个data_。它的定义如下:
__Alloc<N,unsigned long> data_;
//unsigned long data_[N];
下面的代码很好的解释了data_的本质。实际上这2行代码可以相互替换,不影响HugeNumberBase的行为。数据的高位存于data_[0]内,低位存于data_[N-1]内,这是一个设计决定,对HugeNumberBase的性能并不会有太大影响。
__Alloc就是我前面介绍的DataAllocation.h中的3个类模板之一。其实它作为一个Traits,可以是任何实现了相关接口和功能的类模板,并且我在后来的版本中还实现了采用“Copy On Write”和引用计数策略的Alloc。
我提醒读者注意几个常量的意义和私有的成员函数,因为它们是HugeNumberBase的基石,也是最先被实现的部分,我的讲解也将从它们开始。
1)
————————————————————————
//definition
//private:
template<long N,template<long,typename>class __Alloc>
const long HugeNumberBase<N,__Alloc>::EachBits = 8 * sizeof(unsigned long);
template<long N,template<long,typename>class __Alloc>
const long HugeNumberBase<N,__Alloc>::TotalBits = N * EachBits;
template<long N,template<long,typename>class __Alloc>
const long HugeNumberBase<N,__Alloc>::TotalBytes = N * sizeof(unsigned long);
template<long N,template<long,typename>class __Alloc>
const unsigned long HugeNumberBase<N,__Alloc>::SignBit = unsigned long(1)<<(EachBits-1);
template<long N,template<long,typename>class __Alloc>
void HugeNumberBase<N,__Alloc>::AddOne(long Position){ //no asserts,so be careful
long at(N-1-Position/EachBits);
unsigned long tmp(data_[at]);
if((data_[at--]+=unsigned long(1)<<(Position%EachBits))<tmp)
while(at>=0 && !++data_[at])
--at; //if at<0 then overflow
}
————————————————————————
这是紧接HugeNumberBase定义的一段代码,我以数字来表示一段代码在HugeNumberBase定义后的顺序。
这里对几个常量进行了定义。这个版本的HugeNumberBase是用unsigned long的数组记录数据的,EachBits就是每个unsigned long的比特位长,TotalBits自然就是整个HugeNumberBase能表示的比特位长。SignBit在32位机器上就是unsigned long(0x80000000),用来提取long的符号位。
函数AddOne的作用在申明的时候有描述,即在HugeNumberBase的某个比特位置上加1,位置Position是指从最低比特位为0开始。注意我这里所说的HugeNumberBase,是指一个TotalBits比特的大整数,具体一点就是data_数组。
long at(N-1-Position/EachBits);
unsigned long tmp(data_[at]);
得到需要改变的data_数组下标at,并记录下data_[at]的原始值。
接下来的代码似乎有点复杂,我们从简单的开始。首先应该给data_[at]加上某个数值,我们可以这样写:
data_[at] += unsigned long(1) << (Position % EachBits);
但是这会漏掉一种情况,就是发生“进位”。比如data_[at]是0xFFFF...,在16~32任何比特位加1,都导致进位。此时应该在更高的data_上加1。
那么怎么判断是否发生了“进位”?把新的data_[at]与原始的值比较,如果变大了,那么没有进位;如果反而变小了,则发生了进位。于是我们修改一下这样写:
data_[at] += unsigned long(1) << (Position % EachBits);
if(data_[at] < tmp)
......
接下来应该给data_[at - 1]加1,同样考虑是否进位,并决定是否给data_[at - 2]加1,......
这些都在while循环里。最后我们把代码紧缩一下,就变成了原文的样子(读者可能也发现了,我的代码风格是很“紧缩”的)。