#include <boost/multiprecision/cpp_int.hpp>
namespace boost{ namespace multiprecision{ typedef unspecified-type limb_type; enum cpp_integer_type { signed_magnitude, unsigned_magnitude }; enum cpp_int_check_type { checked, unchecked }; template <std::size_t MinBits = 0, std::size_t MaxBits = 0, cpp_integer_type SignType = signed_magnitude, cpp_int_check_type Checked = unchecked, class Allocator = std::allocator<limb_type> > class cpp_int_backend; // // Expression templates default to et_off if there is no allocator: // template <std::size_t MinBits, std::size_t MaxBits, cpp_integer_type SignType, cpp_int_check_type Checked> struct expression_template_default<cpp_int_backend<MinBits, MaxBits, SignType, Checked, void> > { static const expression_template_option value = et_off; }; typedef number<cpp_int_backend<> > cpp_int; // arbitrary precision integer typedef rational_adaptor<cpp_int_backend<> > cpp_rational_backend; typedef number<cpp_rational_backend> cpp_rational; // arbitrary precision rational number // Fixed precision unsigned types: typedef number<cpp_int_backend<128, 128, unsigned_magnitude, unchecked, void> > uint128_t; typedef number<cpp_int_backend<256, 256, unsigned_magnitude, unchecked, void> > uint256_t; typedef number<cpp_int_backend<512, 512, unsigned_magnitude, unchecked, void> > uint512_t; typedef number<cpp_int_backend<1024, 1024, unsigned_magnitude, unchecked, void> > uint1024_t; // Fixed precision signed types: typedef number<cpp_int_backend<128, 128, signed_magnitude, unchecked, void> > int128_t; typedef number<cpp_int_backend<256, 256, signed_magnitude, unchecked, void> > int256_t; typedef number<cpp_int_backend<512, 512, signed_magnitude, unchecked, void> > int512_t; typedef number<cpp_int_backend<1024, 1024, signed_magnitude, unchecked, void> > int1024_t; // Over again, but with checking enabled this time: typedef number<cpp_int_backend<0, 0, signed_magnitude, checked> > checked_cpp_int; typedef rational_adaptor<cpp_int_backend<0, 0, signed_magnitude, checked> > checked_cpp_rational_backend; typedef number<cpp_rational_backend> checked_cpp_rational; // Checked fixed precision unsigned types: typedef number<cpp_int_backend<128, 128, unsigned_magnitude, checked, void> > checked_uint128_t; typedef number<cpp_int_backend<256, 256, unsigned_magnitude, checked, void> > checked_uint256_t; typedef number<cpp_int_backend<512, 512, unsigned_magnitude, checked, void> > checked_uint512_t; typedef number<cpp_int_backend<1024, 1024, unsigned_magnitude, checked, void> > checked_uint1024_t; // Fixed precision signed types: typedef number<cpp_int_backend<128, 128, signed_magnitude, checked, void> > checked_int128_t; typedef number<cpp_int_backend<256, 256, signed_magnitude, checked, void> > checked_int256_t; typedef number<cpp_int_backend<512, 512, signed_magnitude, checked, void> > checked_int512_t; typedef number<cpp_int_backend<1024, 1024, signed_magnitude, checked, void> > checked_int1024_t; }} // namespaces
通常通过上面给出的方便的类型别名来使用 cpp_int_backend
类型。
这个后端是整数类型的“瑞士军刀”,因为它可以表示固定精度和任意精度的整数类型,以及有符号和无符号类型。它有五个模板参数:
确定在诉诸动态内存分配之前,直接存储在对象中的位数。当设置为零时,此字段将根据联合体中可以存储的位数以及动态存储头自动确定:设置一个较大的值可能会提高性能,因为较大的整数值将在需要内存分配之前在内部存储。
确定类型中要存储的最大位数:从而产生一个固定精度类型。当此值与 MinBits 相同,则忽略 Allocator 参数,因为永远不会执行动态内存分配:在这种情况下,Allocator 参数应设置为类型 void
。请注意,不应仅使用此参数来防止大型内存分配,因为不仅分配器可以更好地执行此功能,而且固定精度整数比预期更频繁地分配 MaxBits 的所有存储空间。
确定结果类型是否有符号。请注意,对于任意精度类型,此参数必须是 signed_magnitude
。对于固定精度类型,此类型可以是 signed_magnitude
或 unsigned_magnitude
。
此参数有两个值:checked
或 unchecked
。请参见下文。
用于动态内存分配的分配器,或者当 MaxBits == MinBits 时为类型 void
。
当模板参数 Checked 设置为 checked
时,结果是一个受检查的整数,受检查和不受检查的整数具有以下属性:
条件 |
Checked-Integer |
Unchecked-Integer |
---|---|---|
固定精度算术中的数值溢出 |
抛出 |
执行模 2MaxBits 的算术运算。 |
将整数从无法在目标类型中表示的值构造。 |
抛出 |
将值转换为模 2MaxBits,有符号到无符号的转换会提取输入值的 2 的补码表示的最后 MaxBits 位。 |
无符号减法产生负值。 |
抛出 |
产生将无符号类型视为 2 的补码有符号类型时产生的值。 |
在负值上尝试位运算。 |
抛出 |
产生运算结果的值,而不是位模式,就好像该操作是在 2 的补码整数类型上执行的一样。 |
![]() |
注意 |
---|---|
当由于使用受检查类型而抛出异常时,捕获异常后目标操作数的值将是不确定的。 |
使用此类型时应了解的事项
cpp_int_backend
的值为零。std::overflow_error
。std::runtime_error
。cpp_int_backend
的精度必然有限,因此应注意避免在此类型中使用数值溢出,除非您确实想要模运算行为。int128_t
类型具有 128 位精度,外加一个符号位。在这方面,这些类型的行为与基本(内置) 2 的补码类型不同。可能会想改用 127 位类型,实际上这确实有效,但其行为仍与 2 的补码基本(内置)类型略有不同,因为最小值和最大值相同(除了符号),而对于真正的 2 的补码类型它们相差一。应注意,从 C++20 开始,基本(内置)类型被要求为 2 的补码,因此这是关于内部表示的一个细微差别。std::runtime_error
。这是符号-数值表示的直接结果。[checked_][u]intXXX_t
禁用了表达式模板支持——无论是否启用,对这些类型的性能影响似乎都不大——因此我们不妨通过禁用该功能来获得更快的编译时间。uint128_t
,那么 uint128_t(1)-4
将产生类型为 uint128_t
的值 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
。然而,如果此操作是在 checked_uint128_t
上执行的,则会抛出 std::range_error
。number
实例化也具有移动感知能力。cpp_int_backend
将切换到“琐碎”实现,其中它只是一个单整数的薄包装器。请注意,它仍然会比裸本机整数稍慢,因为它模拟的是符号-数值表示,而不是简单地使用平台的本机符号表示:这确保了随着 cpp_int 的增大,行为不会发生剧烈变化。cpp_int
对 constexpr
值和用户定义的字面量有一些支持,完整描述请参见此处。例如,0xfffff_cppi1024
指定了一个 1024 位整数,值为 0xffff。这可用于生成无法放入任何基本(内置)数字类型中的编译时常量。abs
、swap
、multiply
、add
、subtract
、divide_qr
、integer_modulus
、powm
、lsb
、msb
、bit_test
、bit_set
、bit_unset
、bit_flip
、sqrt
、gcd
、lcm
都得到支持。使用cpp_int 以这种方式需要 C++2a 编译器(支持 std::is_constant_evaluated()
的编译器),或者 GCC-6 或更高版本以 C++14 模式运行。除 GCC 以外且不支持 std::is_constant_evaluated()
的编译器将支持非常有限的操作集:很容易遇到障碍。import_bits
和 export_bits
函数将 cpp_int 的原始位导入/导出到外部存储。更多信息请参见关于导入/导出的部分。#include <boost/multiprecision/cpp_int.hpp> #include <iostream> int main() { using namespace boost::multiprecision; int128_t v = 1; // Do some fixed precision arithmetic: for(unsigned i = 1; i <= 20; ++i) v *= i; std::cout << v << std::endl; // prints 2432902008176640000 (i.e. 20!) // Repeat at arbitrary precision: cpp_int u = 1; for(unsigned i = 1; i <= 100; ++i) u *= i; // prints 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 (i.e. 100!) std::cout << u << std::endl; return 0; }