区块链数据类型之大整数
在Bitcoin Core项目中,由于存在各种各样数值非常大的字段,比如地址,交易和区块的hash,难度等,位数较多以至于无法用已有的内建类型表示出,故定义了class base_uint数据类型,用来存大整数。
数据成员如下:
class base_uint
{
protected:
enum { WIDTH=BITS/32 };
uint32_t pn[WIDTH];
};```
从代码中能看出它是把很大的数据分割成多个```uint32_t```的,比如```base_uint<128>```就占了4个```uint32_t```空间。部分重要的函数如下:
class base_uint
{
public:
base_uint()
{
for (int i = 0; i < WIDTH; i++)
pn[i] = 0;
}
base_uint(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] = b.pn[i];
}
base_uint& operator=(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] = b.pn[i];
return *this;
}
base_uint(uint64_t b)
{
pn[0] = (unsigned int)b;
pn[1] = (unsigned int)(b >> 32);
for (int i = 2; i < WIDTH; i++)
pn[i] = 0;
}
};```
以上是构造函数,比较简单。
以下是部分比较重要的函数:
base_uint& operator=(uint64_t b)
{
pn[0] = (unsigned int)b;
pn[1] = (unsigned int)(b >> 32);
for (int i = 2; i < WIDTH; i++)
pn[i] = 0;
return *this;
}
base_uint& operator+=(const base_uint& b)
{
uint64_t carry = 0;
for (int i = 0; i < WIDTH; i++)
{
uint64_t n = carry + pn[i] + b.pn[i];
pn[i] = n & 0xffffffff;
carry = n >> 32;
}
return *this;
}
由此可知pn[0]存放了低位,算术运算时需要注意进位。
template <unsigned int BITS>
int base_uint<BITS>::CompareTo(const base_uint<BITS>& b) const
{
for (int i = WIDTH - 1; i >= 0; i--) {
if (pn[i] < b.pn[i])
return -1;
if (pn[i] > b.pn[i])
return 1;
}
return 0;
}
template <unsigned int BITS>
bool base_uint<BITS>::EqualTo(uint64_t b) const
{
for (int i = WIDTH - 1; i >= 2; i--) {
if (pn[i])
return false;
}
if (pn[1] != (b >> 32))
return false;
if (pn[0] != (b & 0xfffffffful))
return false;
return true;
}
以上两个是比较,按照常识从最高位往低位比较。
template <unsigned int BITS>
base_uint<BITS>& base_uint<BITS>::operator<<=(unsigned int shift)
{
base_uint<BITS> a(*this);
for (int i = 0; i < WIDTH; i++)
pn[i] = 0;
int k = shift / 32;
shift = shift % 32;
for (int i = 0; i < WIDTH; i++) {
if (i + k + 1 < WIDTH && shift != 0)
pn[i + k + 1] |= (a.pn[i] >> (32 - shift));
if (i + k < WIDTH)
pn[i + k] |= (a.pn[i] << shift);
}
return *this;
}
template <unsigned int BITS>
base_uint<BITS>& base_uint<BITS>::operator>>=(unsigned int shift)
{
base_uint<BITS> a(*this);
for (int i = 0; i < WIDTH; i++)
pn[i] = 0;
int k = shift / 32;
shift = shift % 32;
for (int i = 0; i < WIDTH; i++) {
if (i - k - 1 >= 0 && shift != 0)
pn[i - k - 1] |= (a.pn[i] << (32 - shift));
if (i - k >= 0)
pn[i - k] |= (a.pn[i] >> shift);
}
return *this;
}
比如第二个右移解释下过程,由于是无符号数,那么右移时高位添0,比如以十六进制为例0x0000 0000 0000 ce43 948b f34b ea34 3443 88bc,如果右移为32时[整除],结果为0x0000 0000 000 0000 ce43 948b f34b ea34 3443,k=1,shift=0,第一个if都不执行;在i为0时,第二个if不执行,当i=1时,第二个if执行,此时 pn[0]=3443,当i=2时,pn[1]=ea34以此类推,其中k的作用是控制修改哪些uint32_t空间,即最高位k个uint32_t空间以0代替,即都被以0代替。
再如右移16位[有余],那么k为0,第一轮执行时,第一个if的不执行,第二个if执行是后pn[0]=3434,第二轮执行时,第一个if使得pn[0]的高16位变成了pn[1]的低16位,执行后pn[0]=4b34,后面的迭代效果类似。
小结:
以上只列出一部分常用的函数,也没什么复杂的地方,就是对于有些操作,需要注意溢出,截断,有无符号类型的转换,往往会导致难以复现的bug;还有既然是自定义类型,重载了内建类型的操作符,需保留原来的语义。
1229

被折叠的 条评论
为什么被折叠?



