Boost C++ 库

……是全球备受推崇、设计精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu《C++ Coding Standards》

cpp_int - Boost C++ 函数库
PrevUpHomeNext

#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

确定在诉诸动态内存分配之前,直接存储在对象中的位数。当设置为零时,此字段将根据联合体中可以存储的位数以及动态存储头自动确定:设置一个较大的值可能会提高性能,因为较大的整数值将在需要内存分配之前在内部存储。

MaxBits

确定类型中要存储的最大位数:从而产生一个固定精度类型。当此值与 MinBits 相同,则忽略 Allocator 参数,因为永远不会执行动态内存分配:在这种情况下,Allocator 参数应设置为类型 void。请注意,不应仅使用此参数来防止大型内存分配,因为不仅分配器可以更好地执行此功能,而且固定精度整数比预期更频繁地分配 MaxBits 的所有存储空间。

SignType

确定结果类型是否有符号。请注意,对于任意精度类型,此参数必须是 signed_magnitude。对于固定精度类型,此类型可以是 signed_magnitudeunsigned_magnitude

Checked

此参数有两个值:checkedunchecked。请参见下文。

Allocator

用于动态内存分配的分配器,或者当 MaxBits == MinBits 时为类型 void

当模板参数 Checked 设置为 checked 时,结果是一个受检查的整数,受检查和不受检查的整数具有以下属性:

条件

Checked-Integer

Unchecked-Integer

固定精度算术中的数值溢出

抛出 std::overflow_error

执行模 2MaxBits 的算术运算。

将整数从无法在目标类型中表示的值构造。

抛出 std::range_error

将值转换为模 2MaxBits,有符号到无符号的转换会提取输入值的 2 的补码表示的最后 MaxBits 位。

无符号减法产生负值。

抛出 std::range_error

产生将无符号类型视为 2 的补码有符号类型时产生的值。

在负值上尝试位运算。

抛出 std::range_error

产生运算结果的值,而不是位模式,就好像该操作是在 2 的补码整数类型上执行的一样。

[Note] 注意

当由于使用受检查类型而抛出异常时,捕获异常后目标操作数的值将是不确定的。

使用此类型时应了解的事项

  • 默认构造的 cpp_int_backend 的值为零。
  • 除以零将导致抛出 std::overflow_error
  • 从包含无效非数字字符的字符串进行构造将导致抛出 std::runtime_error
  • 由于当分配器参数为 void 时 cpp_int_backend 的精度必然有限,因此应注意避免在此类型中使用数值溢出,除非您确实想要模运算行为。
  • 该类型在内部使用符号-数值表示,因此 int128_t 类型具有 128 位精度,外加一个符号位。在这方面,这些类型的行为与基本(内置) 2 的补码类型不同。可能会想改用 127 位类型,实际上这确实有效,但其行为仍与 2 的补码基本(内置)类型略有不同,因为最小值和最大值相同(除了符号),而对于真正的 2 的补码类型它们相差一。应注意,从 C++20 开始,基本(内置)类型被要求为 2 的补码,因此这是关于内部表示的一个细微差别。
  • 尝试将负值作为八进制或十六进制字符串打印将导致抛出 std::runtime_error。这是符号-数值表示的直接结果。
  • 固定精度类型 [checked_][u]intXXX_t 禁用了表达式模板支持——无论是否启用,对这些类型的性能影响似乎都不大——因此我们不妨通过禁用该功能来获得更快的编译时间。
  • 无符号类型支持减法——结果“如同”执行了 2 的补码运算,只要它们不是受检查的整数(见上文)。换句话说,在这种情况下,它们的行为与基本(内置)整数类型的行为几乎相同。例如,如果我们使用 uint128_t,那么 uint128_t(1)-4 将产生类型为 uint128_t 的值 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD。然而,如果此操作是在 checked_uint128_t 上执行的,则会抛出 std::range_error
  • 无符号类型的单目否定会导致编译器错误(静态断言)。
  • 此后端支持右值引用,并且是可移动感知(move-aware)的,这使得基于此后端的 number 实例化也具有移动感知能力。
  • 当以固定精度使用时,该类型的大小总是比您预期的 N 位整数大一个机器字(加上任何编译器应用的对齐填充):额外的字存储了符号以及整数中有多少个机器字实际上在使用中。后者是针对较大固定精度整数的一种优化,因此一个 1024 位整数的性能特征几乎与 128 位整数相同,而不是在加法上慢 4 倍,在乘法上慢 16 倍(假设涉及的值始终适合 128 位)。通常这意味着您可以使用足够宽以应对“最坏情况”的整数类型,而性能损失很小,即使大多数时候算术实际上可以用更窄的类型来完成。另请注意,足够小以适合最大本机整数类型的无符号固定精度类型将成为该类型的简单包装器,包括“受检查”的变体。小的有符号类型将始终有一个额外的符号字,因此比其本机等效类型要大。
  • 当以固定精度使用且 MaxBits 小于最大本机整数类型中的位数时,内部 cpp_int_backend 将切换到“琐碎”实现,其中它只是一个单整数的薄包装器。请注意,它仍然会比裸本机整数稍慢,因为它模拟的是符号-数值表示,而不是简单地使用平台的本机符号表示:这确保了随着 cpp_int 的增大,行为不会发生剧烈变化。
  • 固定精度 cpp_intconstexpr 值和用户定义的字面量有一些支持,完整描述请参见此处。例如,0xfffff_cppi1024 指定了一个 1024 位整数,值为 0xffff。这可用于生成无法放入任何基本(内置)数字类型中的编译时常量。
  • cpp_int 类型支持 constexpr 算术,前提是它是一个没有分配器的固定精度类型。它也可以是受检查的整数:在这种情况下,溢出或未定义行为将生成编译器错误。此外,自由函数 absswapmultiplyaddsubtractdivide_qrinteger_moduluspowmlsbmsbbit_testbit_setbit_unsetbit_flipsqrtgcdlcm 都得到支持。使用cpp_int 以这种方式需要 C++2a 编译器(支持 std::is_constant_evaluated() 的编译器),或者 GCC-6 或更高版本以 C++14 模式运行。除 GCC 以外且不支持 std::is_constant_evaluated() 的编译器将支持非常有限的操作集:很容易遇到障碍。
  • 您可以通过 import_bitsexport_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;
}

PrevUpHomeNext