众所周知,C++ 内置类型 uint32_t、int32_t、uint64_t、int64_t 甚至 GCC/CLang 内置类型 __int128 和 unsigned __int128 的大小都正好等于位宽除以 8。
但是,如果您打印 Boost 的
boost::multiprecision::uint256_t
或 uint512_t
的 sizeof,那么 uint256_t 将得到 48 个字节而不是 32 个字节,uint512_t 将得到 80 个字节而不是 64 个字节。这两种类型的 sizeof 都比预期多了 16 个字节。 在这里查看演示.
但是
boost::multiprecision::uint128_t
的 sizeof 恰好给出了预期的 16 个字节。
看起来 Boost 的所有整数的基类
cpp_int_base
有几个字段:
data_type m_data;
unsigned m_limbs;
bool m_sign, m_internal, m_alias;
只有 m_data 字段包含整数位,而其他字段给了这额外的 16 个不必要的 sizeof 字节。
我的问题是否有可能以某种方式调整 Boost 多精度整数,使其仅包含数据位而没有其他内容?
换句话说,它保持符号(如果它是有符号整数)与 Intel CPU 将它保持在 int64_t 中相同,基本上最高位是符号位,其余位是编码符号补充形式。因此,Boost 整数的编码与默认的 intel uint64_t、int64_t 相同。
如果你查看模板的签名
cpp_int_base
然后你会看到:
template <unsigned MinBits, unsigned MaxBits, cpp_integer_type SignType, cpp_int_check_type Checked,
class Allocator, bool trivial = false>
struct cpp_int_base;
显然
trivial
似乎几乎做了必要的事情,cpp_int_base 的特化与 trivial = true 只包含两个字段:
local_limb_type m_data;
bool m_sign;
所以只比最小可能的 sizeof 大 1 个字节。
大于 128 位的整数默认有 trivial = true,而更大的整数有 trivial = false。
但是没有办法控制
trivial
模板参数,因为如果你看一下 uint256_t 定义然后你会看到:
using uint256_t = number<cpp_int_backend<256, 256, unsigned_magnitude, unchecked, void> > ;
并且 cpp_int_backend 在它的模板参数中没有
trivial
参数,只有 cpp_int_base 有这个微不足道的内部。但是用户无法访问 cpp_int_base,它是库的内部细节。
我也不知道 128 位整数如何恰好有 16 个字节的 sizeof,因为正如我在上面显示的那样,即使是微不足道的参数也有额外的
bool m_sign;
字段,它应该提供额外的 1 个字节(即 17 个 sizeof)。但是不知何故,128 位整数不是预期的 17 个字节,而是 16 个字节。
为什么我需要 boost 整数来拥有最少的位数。因为在我的程序中我有数百万个整数数组。除了通常的数学运算之外,我还对这些整数进行了自己的特殊数学运算。我的数学运算适用于通常的英特尔形式的整数,与表示的 int64_t 和 uint64_t 相同。但有时我需要像
+ - * / % ^ | ~
这样的常规操作,而不是实现它们我决定使用Boost多精度库。
如果 Boost 具有与 Intel 完全相同的表示,我会做
reinterpret_cast<boost::multiprecision::uint256_t &>(array[i]) *= 12345;
,没有任何中间转换或 memcpy。但是由于 Boost 有不同的格式,我必须来回编写自定义转换。
如果我这样做
^
操作例如 256 位整数需要 1-4 个 CPU 周期。而且,如果我与 Boost 格式进行转换,那么将需要 5-10 个周期,这是一个非常大的开销。
因此,我需要这种普通的 Boost 整数格式作为优化,而不是对每个操作都进行转换。
另一个不太重要的原因是,在我的模板代码中的某个地方,我需要弄清楚一些模板类型 T 的位宽是多少。如果这始终是一个普通的 Intel 格式,那么
sizeof(T) * 8
将给出数字的精确位宽。对于 Boost 格式,我需要专门化一些辅助模板结构,例如 BitWidthOf<T>::value
.