区块链数据类型之大整数

区块链数据类型之大整数

在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;还有既然是自定义类型,重载了内建类型的操作符,需保留原来的语义。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值