| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
wide-integer implements a generic C++ template for extended width unsigned and signed integral types.
This C++ template header-only library implements drop-in big integer types such as uint128_t, uint256_t, uint384_t, uint512_t, uint1024_t, uint1536_t, etc. These can be used essentially like regular built-in integers. Corresponding signed integer types such as int128_t, int256_t, and the like can also be used.
The big integer class is called math::wide_integer::uintwide_t (i.e., uintwide_t residing in the namespace math::wide_integer), as shown in greater detail below.
wide-integer supports both unsigned as well as signed integral types having width of $1 {\ldots} 63 {\times} 2^N$ while being $16$, $24$, $32$ or larger. In addition, small integer types such as software-synthesized versions of uint24_t, uint48_t, uint64_t, uint96_t, uint128_t, etc. (or signed counterparts of these) can also be created with wide-integer.
We also emphasize here that less-common types (i.e., those less common than, say, uint128_t, uint256_t, uint512_t, etc.) can also be synthesized. Types such as uint80_t made from five 16-bit limbs, or uint96_t composed of three 32-bit limbs, or other similar types can be readily synthesized with wide-integer.
wide-integer also features basic realizations of several elementary and number theoretical functions such as root finding, random distribution, Miller-Rabin primality testing, greatest common denominator (GCD), least common multiplier (LCM), integer division divmod(), proposed for C++29 as div_rem_to_neg_inf(), and more.
Inclusion of a single C++14 header file is all that is needed for using wide-integer, as shown in the examples.
When working in your own project with wide-integer, using the uintwide_t.h header is straightforward. Identify the header within its directory. Include this header path to the compiler's set of include paths or in your project. Then simply #include <uintwide_t.h> in the normal C++ way.
Easy application follows via traditional C-style typedef or alias such as uint512_t. An instance of the defined type can be used very much like a built-in integral type.
In the following code, for example, the static uint512_t variable x is initialized with unsigned, integral value 3U. The main subroutine subsequently computes $3^{301}$ with the specialized wide-integer, namespace-specific function pow, which is found via ADL.
The approximate result is
$$3^{301}~{\approx}~4.10674{\times}~10^{143}\text{.}$$
See also the following informative links to Wolfram Alpha(R).
This example, compiled with successful output result, is shown in its entirety in the following short link to godbolt.
In particular,
#include <math/wide_integer/uintwide_t.h>
#include <iostream>
auto main() -> int
{
using uint512_t = ::math::wide_integer::uintwide_t<512U, std::uint32_t>;
const uint512_t x { 3U };
const auto p3 = pow(x, 301);
// 410674437175765127973978082146264947899391086876012309414440570235106991532497229781400618467066824164751453321793982128440538198297087323698003
std::cout << "p3: " << p3 << std::endl;
std::cout << "Cast p3 to double: " << double { p3 } << std::endl;
}The code sequence above defines the local data type uint512_t with an alias. The first template parameter 512U sets the binary width (or bit count) while the second optional template parameter std::uint32_t sets the internal limb type. The limb type must be unsigned and one of std::uint8_t, std::uint16_t, std::uint32_t or on some systems std::uint64_t. If the second template parameter LimbType is left blank, the default limb type (i.e., uint_defaultlimb_t) is either std::uint32_t or std::uint64_t, with $64$-bit limbs if WIDE_INTEGER_HAS_LIMB_TYPE_UINT64 is defined (see also the relevant docs below).
The complete template signature of the uintwide_t class is shown below.
namespace math::wide_integer {
namespace detail { using size_t = std::uint32_t; }
using detail::size_t;
// Forward declaration of the uintwide_t template class.
template<const size_t Width2,
typename LimbType = uint_defaultlimb_t,
typename AllocatorType = void,
const bool IsSigned = false>
class uintwide_t;
// Here, uint_defaultlimb_t is either std::uint32_t or std::uint64_t
// (if WIDE_INTEGER_HAS_LIMB_TYPE_UINT64, see below) is defined.
} // namespace math::wide_integeruintwide_t also has a third optional template parameter that is used to set the allocator type employed for internal storage of the big integer's data. The default allocator type is void and uintwide_t uses stack allocation with an std::array-like internal representation. Setting the allocator type to an actual allocator such as, for instance, std::allocator<limb_type> activates allocator-based internal storage for uintwide_t. Using allocator-based storage reduces stack consumption and can be especially beneficial for higher digit counts. For low digit counts, the allocator type can simply be left blank (thus defaulting to void) or explicitly be set to void and stack allocation will be used in either case.
If an allocator is supplied with any granularity other than limb_type (in other words LimbType) such as std::allocator<void>, custom_allocator_type<char>, etc., then the uintwide_t class will internally rebind the allocator to the granularity and unsigned-ness of limb_type using rebind_alloc from std::allocator_traits.
The fourth template parameter IsSigned can be set to true to activate a signed integer type. If left blank, the default value of IsSigned is false and the integer type will be unsigned.
Various interesting and algorithmically challenging examples have been implemented. It is hoped that the examples provide inspiration and guidance on how to use wide-integer.
The recent status of building and executing the tests and examples in Continuous Integration (CI) is always shown in the Build Status banner. Additional banners from other syntax checks and builds may also be visible.
It is also possible, if desired, to build and execute the tests and examples using various different OS/compiler combinations.
Building and running the tests and examples can be accomplished using the Microsoft VisualStudio solution workspace provided in wide_integer.sln, wide_integer_vs2022.sln, etc. The MSVC solution file(s) are located in the project's root directory.
You can also build and run tests and examples from an empty directory using CMake. Follow the CMake pattern:
cmake /path/to/wide-integer
cmake --build .
ctest --verboseAlternatively building the tests and examples with native GCC (i.e., on *nix) can be executed with a simple, but rather lengthy, command line entered manually from the command shell. Consider, for instance, building in Linux with GCC in the presence of unsigned __int128. Furthermore, the Boost.Multiprecision library is used for some examples and tests. In this build example, Boost is intended to be located in the made-up directory ../boost-root, which needs to be adapted according to the actual location of Boost. The command line below illustrates how to build all of the wide_integer tests and examples directly from the *nix command line.
cd wide_integer
g++ \
-finline-functions \
-finline-limit=32 \
-O3 \
-Wall \
-Wextra \
-Wpedantic \
-Wconversion \
-Wsign-conversion \
-Wno-maybe-uninitialized \
-Wno-cast-function-type \
-std=c++14 \
-DWIDE_INTEGER_HAS_LIMB_TYPE_UINT64 \
-I. \
-I../boost-root \
-pthread \
-lpthread \
test/test.cpp \
test/test_uintwide_t_boost_backend.cpp \
test/test_uintwide_t_edge_cases.cpp \
test/test_uintwide_t_examples.cpp \
test/test_uintwide_t_float_convert.cpp \
test/test_uintwide_t_int_convert.cpp \
test/test_uintwide_t_n_base.cpp \
test/test_uintwide_t_n_binary_ops_base.cpp \
test/test_uintwide_t_spot_values.cpp \
examples/example000_numeric_limits.cpp \
examples/example000a_builtin_convert.cpp \
examples/example001_mul_div.cpp \
examples/example001a_div_mod.cpp \
examples/example002_shl_shr.cpp \
examples/example003_sqrt.cpp \
examples/example003a_cbrt.cpp \
examples/example004_rootk_pow.cpp \
examples/example005_powm.cpp \
examples/example005a_pow_factors_of_p99 \
examples/example006_gcd.cpp \
examples/example007_random_generator.cpp \
examples/example008_miller_rabin_prime.cpp \
examples/example008a_miller_rabin_prime.cpp \
examples/example008b_solovay_strassen_prime \
examples/example009_timed_mul.cpp \
examples/example009a_timed_mul_4_by_4.cpp \
examples/example009b_timed_mul_8_by_8.cpp \
examples/example010_uint48_t.cpp \
examples/example011_uint24_t.cpp \
examples/example012_rsa_crypto.cpp \
examples/example013_ecdsa_sign_verify.cpp \
examples/example014_pi_spigot_wide.cpp \
-o wide_integer.exeThere is straightforward GNUmake support via Makefile. The Makefile supports various command-line options that allow tuning the compiler, language standard, Boost's root directory, etc.
A sample command line for building the executable wide_integer with the Makefile is shown below.
make MY_CC=clang++ MY_STD=c++23 MY_BOOST_ROOT=../boost-root allTesting is definitely a big issue. A growing, supported test suite improves confidence in the library. It provides for tested, efficient functionality on the PC and workstation. The GitHub code is, as mentioned above, delivered with an affiliated MSVC project or a variety of other build/make options that use easy-to-understand subroutines called from main(). These exercise the various examples and the full suite of test cases.
If an issue is reported, reproduced and verified, an attempt is made to correct it without breaking any other code. Upon successful correction, specific test cases exercising the reported issue are usually added as part of the issue resolution process.
CI runs on both push-to-branch as well as pull request using GitHub Actions. Various compilers, operating systems, and C++ standards ranging from C++14, 17, 20, 23 are included in CI.
In CI, we use both elevated GCC/clang compiler warnings as well as MSVC level 4 warnings active on the corresponding platforms. For additional in-depth syntax checking, clang-tidy is used both in CI as well as in offline checks to improve static code quality.
GCC's run-time sanitizers are used in CI in order to help assure dynamic quality. This effort also includes fuzzing with libFuzzer.
Additional quality checks are performed on pull-request and merge to master using modern third party open-source services. These include CodeQL, Synopsis Coverity, and CodeSonar. At the moment, the Coverity check is run with manual report submission. Automation of this is, however, planned.
Code coverage uses GCC/gcov/lcov and has a quality-gate with comparison/baseline-check provided by Codecov.
Quality badges are displayed at the top of this repository's readme page.
We will now present various straightforward detailed examples.
The code below performs some elementary algebraic calculations with a simple mixture of 256-bit and 512-bit unsigned integral types.
This example, compiled with successful output result, is shown in its entirety in the following short link to godbolt.
#include <math/wide_integer/uintwide_t.h>
#include <iomanip>
#include <iostream>
#include <sstream>
auto main() -> int
{
using uint256_t = ::math::wide_integer::uint256_t;
using uint512_t = ::math::wide_integer::uint512_t;
// Construction from string. Additional constructors
// are available from other built-in types.
const uint256_t a("0xF4DF741DE58BCB2F37F18372026EF9CBCFC456CB80AF54D53BDEED78410065DE");
const uint256_t b("0x166D63E0202B3D90ECCEAA046341AB504658F55B974A7FD63733ECF89DD0DF75");
// Elementary arithmetic operations.
const uint512_t c = (uint512_t(a) * uint512_t(b));
const uint256_t d = (a / b);
// Logical comparison.
const auto result_is_ok = ( (c == "0x1573D6A7CEA734D99865C4F428184983CDB018B80E9CC44B83C773FBE11993E7E491A360C57EB4306C61F9A04F7F7D99BE3676AAD2D71C5592D5AE70F84AF076")
&& (d == "0xA"));
// Print the hexadecimal representation string output.
std::stringstream strm;
strm << "0x" << std::hex << std::uppercase << c << '\n';
strm << "0x" << std::hex << std::uppercase << d << '\n';
// Visualize if the result is OK.
strm << "result_is_ok: " << std::boolalpha << result_is_ok;
std::cout << strm.str() << std::endl;
}wide-integer also supports a small selection of number-theoretical functions such as least and most significant bit, square root, $k^{th}$ root, power, power-modulus, greatest common denominator and random number generation. These functions are found via ADL.
The example below calculates an integer square root.
This example, compiled with successful output result, is shown in its entirety in the following short link to godbolt.
#include <math/wide_integer/uintwide_t.h>
#include <iomanip>
#include <iostream>
auto main() -> int
{
using uint256_t = ::math::wide_integer::uint256_t;
const uint256_t a("0xF4DF741DE58BCB2F37F18372026EF9CBCFC456CB80AF54D53BDEED78410065DE");
const uint256_t s = sqrt(a);
const auto result_is_ok = (s == "0xFA5FE7853F1D4AD92BDF244179CA178B");
const auto flg = std::cout.flags();
std::cout << "result_is_ok: " << std::boolalpha << result_is_ok << std::endl;
std::cout.flags(flg);
return (result_is_ok ? 0 : -1);
}The following sample performs add, subtract, multiply and divide of uint48_t. See this example also in the following short link to godbolt.
#include <math/wide_integer/uintwide_t.h>
#include <iomanip>
#include <iostream>
#include <random>
#include <sstream>
auto main() -> int
{
using uint48_t = ::math::wide_integer::uintwide_t<static_cast<math::wide_integer::size_t>(UINT32_C(48)), std::uint8_t>;
using distribution_type = ::math::wide_integer::uniform_int_distribution<static_cast<math::wide_integer::size_t>(UINT32_C(48)), typename uint48_t::limb_type>;
using random_engine_type = std::linear_congruential_engine<std::uint32_t, UINT32_C(48271), UINT32_C(0), UINT32_C(2147483647)>;
random_engine_type generator(static_cast<std::uint32_t>(UINT32_C(0xF00DCAFE))); // NOLINT(cert-msc32-c,cert-msc51-cpp,cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
distribution_type distribution;
const auto a64 = static_cast<std::uint64_t>(distribution(generator));
const auto b64 = static_cast<std::uint64_t>(distribution(generator));
const uint48_t a(a64);
const uint48_t b(b64);
const uint48_t c_add = (a + b);
const uint48_t c_sub = (a - b);
const uint48_t c_mul = (a * b);
const uint48_t c_div = (a / b);
const auto result_is_ok = ( ( (c_add == static_cast<std::uint64_t>((a64 + b64) & static_cast<std::uint64_t>(UINT64_C(0x0000FFFFFFFFFFFF))))
&& (c_sub == static_cast<std::uint64_t>((a64 - b64) & static_cast<std::uint64_t>(UINT64_C(0x0000FFFFFFFFFFFF))))
&& (c_mul == static_cast<std::uint64_t>((a64 * b64) & static_cast<std::uint64_t>(UINT64_C(0x0000FFFFFFFFFFFF))))
&& (c_div == static_cast<std::uint64_t>((a64 / b64) & static_cast<std::uint64_t>(UINT64_C(0x0000FFFFFFFFFFFF)))))
&&
( (static_cast<std::uint64_t>(c_add) == static_cast<std::uint64_t>((a64 + b64) & static_cast<std::uint64_t>(UINT64_C(0x0000FFFFFFFFFFFF))))
&& (static_cast<std::uint64_t>(c_sub) == static_cast<std::uint64_t>((a64 - b64) & static_cast<std::uint64_t>(UINT64_C(0x0000FFFFFFFFFFFF))))
&& (static_cast<std::uint64_t>(c_mul) == static_cast<std::uint64_t>((a64 * b64) & static_cast<std::uint64_t>(UINT64_C(0x0000FFFFFFFFFFFF))))
&& (static_cast<std::uint64_t>(c_div) == static_cast<std::uint64_t>((a64 / b64) & static_cast<std::uint64_t>(UINT64_C(0x0000FFFFFFFFFFFF))))));
std::stringstream strm { };
strm << "result_is_ok: " << std::boolalpha << result_is_ok;
std::cout << strm.str() << std::endl;
}The next example computes the real-valued cube root of $10^{3,333}$. The real-valued cube root of this very large unsigned integer is $10^{1,111}$. We will use the (somewhat uncommon) integral data type uint11264_t. Since uint11264_t has approximately $3,390$ decimal digits of precision, it is large enough to hold the value of $10^{3,333}$ prior to (and following) the cube root operation.
See this example fully worked out at the following short link to godbolt
#include <math/wide_integer/uintwide_t.h>
#include <iomanip>
#include <iostream>
#include <sstream>
auto main() -> int
{
using uint11264_t = ::math::wide_integer::uintwide_t<11264U, std::uint32_t>;
// Create the string '1' + 3,333 times '0', which is
// equivalent to the decimal integral value 10^3333.
const std::string str_a = "1" + std::string(3333U, '0');
const uint11264_t a = str_a.data();
const uint11264_t s = cbrt(a);
// Create the string '1' + 1,111 times '0', which is
// equivalent to the decimal integral value 10^1111.
// (This is the cube root of 10^3333.)
const std::string str_control = "1" + std::string(1111U, '0');
const auto result_is_ok = (s == uint11264_t(str_control.data()));
std::stringstream strm { };
strm << s;
strm << "\nresult_is_ok: " << std::boolalpha << result_is_ok;
std::cout << strm.str() << std::endl;
}Wide-Integer has been tested with numerous compilers, for target systems ranging from eight to sixty-four bits. The library is specifically designed for efficiency with small to medium bit counts. Supported bit counts include integers $1 {\ldots} 63 {\times} 2^N$ while being $16$, $24$, $32$ or larger such as $256$, $384$, $512$, $768$, $1024$, or other less common bit counts such as $11,264$, etc.
Small, medium and large bit counts are supported. Common applications might use the range of uint128_t, uint256_t or uint512_t. It is also possible to make software-synthesized (not very efficient) versions of uint24_t, uint32_t or uint48_t, which might useful for hardware prototyping or other simulation and verification needs. On the high-digit end, Karatsuba multiplication extends the high performance range to many thousands of bits. Fast long division, however, relies on a classical algorithm and sub-quadratic high-precision division is not yet implemented.
Portability of the code is another key point of focus. Special care has been taken to test in certain high-performance embedded real-time programming environments.
Various configuration features can optionally be enabled or disabled at compile time with the compiler switches:
#define WIDE_INTEGER_DISABLE_IOSTREAM
#define WIDE_INTEGER_DISABLE_TO_STRING
#define WIDE_INTEGER_DISABLE_FLOAT_INTEROP
#define WIDE_INTEGER_DISABLE_IMPLEMENT_UTIL_DYNAMIC_ARRAY
#define WIDE_INTEGER_HAS_LIMB_TYPE_UINT64
#define WIDE_INTEGER_HAS_MUL_8_BY_8_UNROLL
#define WIDE_INTEGER_DISABLE_TRIVIAL_COPY_AND_STD_LAYOUT_CHECKS
#define WIDE_INTEGER_NAMESPACE
#define WIDE_INTEGER_DISABLE_PRIVATE_CLASS_DATA_MEMBERS
#define WIDE_INTEGER_HAS_CLZ_LIMB_OPTIMIZATIONSWhen working with even the most tiny microcontroller systems, I/O streaming can optionally be disabled with the compiler switch:
#define WIDE_INTEGER_DISABLE_IOSTREAMThe default setting is WIDE_INTEGER_DISABLE_IOSTREAM not set and I/O streaming operations are enabled.
Conversion to std::string is supported with the namespace-specific function to_string. This is analogous to the standard library's std::to_string function, but implemented specifically for instances of uintwide_t. wide-integer's local, namespace-specific to_string function (and the inclusion of the necessary <string> header) are both deactivated with:
#define WIDE_INTEGER_DISABLE_TO_STRINGInteroperability with built-in floating-point types such as construct-from, cast-to, binary arithmetic with built-in floating-point types can be optionally disabled by defining:
#define WIDE_INTEGER_DISABLE_FLOAT_INTEROPThe default setting is WIDE_INTEGER_DISABLE_FLOAT_INTEROP not set and all available functions implementing construction-from, cast-to, binary arithmetic with built-in floating-point types are enabled.
#define WIDE_INTEGER_DISABLE_IMPLEMENT_UTIL_DYNAMIC_ARRAYThis macro disables uintwide_t.h's own local implementation of the util::dynamic_array template class. The logic of this macro is negated. Its default setting (of being disabled itself) ensures that standalone uintwide_t.h is free from any additional header dependencies.
The template utility class util::dynamic_array is used as a storage container for certain instantiations of uintwide_t. This macro is disabled by default and uintwide_t.h does actually provide its own local implementation of the util::dynamic_array template class. Otherwise, the header file <util/utility/util_dynamic_array.h> must be found in the include path.
When working on high-performance systems having unsigned __int128 (an extended-width, yet non-standard data type) or std::Unsigned128, a 64-bit limb of type uint64_t can be used. Enable the 64-bit limb type on such systems with the compiler switch:
#define WIDE_INTEGER_HAS_LIMB_TYPE_UINT64or (when using GCC, clang or similar) on the compiler command line with:
-DWIDE_INTEGER_HAS_LIMB_TYPE_UINT64This macro is disabled by default. The template default limb type, uint_defaultlimb_t is either std::uint32_t or std::uint64_t if WIDE_INTEGER_HAS_LIMB_TYPE_UINT64 is defined.
The example below, for instance, uses a 64-bit limb type on GCC or clang.
#define WIDE_INTEGER_HAS_LIMB_TYPE_UINT64
#include <math/wide_integer/uintwide_t.h>
using uint_fast256_t = ::math::wide_integer::uintwide_t<256U, std::uint64_t>;
static uint_fast256_t x = 42U;Another potential optimization macro can be activated with:
#define WIDE_INTEGER_HAS_MUL_8_BY_8_UNROLLThis macro might improve performance on some target/compiler systems by manually unrolling the multiplication loop(s) for uintwide_t instances having eight limbs. This macro is disabled by default.
#define WIDE_INTEGER_DISABLE_TRIVIAL_COPY_AND_STD_LAYOUT_CHECKSThis macro disables compile-time checks for std::is_trivially_copyable and std::is_standard_layout. These checks provide assurance (among other attributes) that uintwide_t's constructors satisfy rules needed for mixed-language C/C++ usage. Some older legacy target/compiler systems might have non-standard or incomplete STL implementations that lack these compile-time templates. For such compilers, it makes sense to deactivate these compile-time checks via activation of this macro. This macro is disabled by default and both the trivially-copyable as well as the standard-layout compile-time checks are active.
#define WIDE_INTEGER_NAMESPACE something_uniqueThis is an advanced macro intended to be used in strict, exacting applications for which using the unqualified, global namespace math (i.e., namespace ::math) is undesired or inacceptable. We recall that all parts of the wide-integer implementation, such as the uintwide_t class and its associated implementation details reside within namespace ::math::wide_integer
Defining the macro WIDE_INTEGER_NAMESPACE to be something like, for instance,
-DWIDE_INTEGER_NAMESPACE=something_uniqueplaces all parts of the wide-integer implementation and its details within the prepended outer namespace something_unique - as in
namespace something_unique::math::wide_integer
{
// ...
}When utilizing the WIDE_INTEGER_NAMESPACE option, the actual name or nesting depth of the desired prepended outer namespace can be varied if (or as) needed for the particular project.
By default the macro WIDE_INTEGER_NAMESPACE is not defined. In this default state, namespace ::math::wide_integer is used and the uintwide_t class and its associated implementation details reside therein.
#define WIDE_INTEGER_DISABLE_PRIVATE_CLASS_DATA_MEMBERSThis optional macro can be used to switch uintwide_t's data member access from private to public. This allows the uintwide_t class to be used as a so-called structured class, such as is needed for constant-valued template parameters in a constexpr context. This preprocessor switch was invented based on the discussion in issue 335
Making private data members public is unusual for some designs. So the preprocessor switch WIDE_INTEGER_DISABLE_PRIVATE_CLASS_DATA_MEMBERS is not defined (i.e., not set) by default. This ensures that uintwide_t's data members remain private by default.
#define WIDE_INTEGER_HAS_CLZ_LIMB_OPTIMIZATIONSThis optional macro activates certain optimizations that count leading zero-limbs prior to classical quadratic multiplication. This may offer performance advantages on some systems by avoiding some potentially costly zero-valued limb-multiplication steps.
This preprocessor switch was motivated by the discussion in issue 362
By default, the preprocessor switch WIDE_INTEGER_HAS_CLZ_LIMB_OPTIMIZATIONS is not defined and CLZ-limb optimizations are default-disabled.
uintwide_t supports C++14, 17, 20, 23 and beyond compile-time constexpr-ness for all constructions, casts, operations, evaluation of function results, etc.
The code below, for instance, shows compile-time instantiations of uintwide_t from character strings with subsequent constexpr evaluations of binary operations multiply, divide, intergal cast and comparison.
See this example fully worked out at the following short link to godbolt. The generated assembly includes nothing other than the call to main() and its subsequent return of the value zero (i.e., main()'s successful return-value in this example).
#include <math/wide_integer/uintwide_t.h>
using uint256_t = ::math::wide_integer::uintwide_t<256U>;
using uint512_t = ::math::wide_integer::uintwide_t<512U>;
// Compile-time construction from string.
constexpr auto a = uint256_t("0xF4DF741DE58BCB2F37F18372026EF9CBCFC456CB80AF54D53BDEED78410065DE");
constexpr auto b = uint256_t("0x166D63E0202B3D90ECCEAA046341AB504658F55B974A7FD63733ECF89DD0DF75");
// Compile time binary arithmetic operations.
constexpr auto c = uint512_t(a) * uint512_t(b);
constexpr auto d = uint256_t(a / b);
// Compile-time comparison.
constexpr auto result_is_ok = ( (c == "0x1573D6A7CEA734D99865C4F428184983CDB018B80E9CC44B83C773FBE11993E7E491A360C57EB4306C61F9A04F7F7D99BE3676AAD2D71C5592D5AE70F84AF076")
&& (static_cast<std::uint_fast8_t>(d) == static_cast<std::uint_fast8_t>(UINT8_C(10))));
// constexpr verification.
static_assert(result_is_ok, "Error: example is not OK!");
auto main() -> int { }Signed big integers are also supported in the wide_integer library. Use the fourth template partameter IsSigned to indicate the signed-ness (or unsigned-ness) of uintwide_t. The code below, for instance, uses an aliased version of signed int256_t.
#include <math/wide_integer/uintwide_t.h>
using int256_t = ::math::wide_integer::uintwide_t<256U, std::uint32_t, void, true>;
const int256_t n1(-3);
const int256_t n2(-3);
// +9
const int256_t n3 = n1 * n2;The following design choices have been implemented when handling negative arguments in number theoretical functions.
The following notable construction/conversion rules have been implemented in the wide-integer project.
For sufficiently modern standards-conforming compilers, namespace-specific functions to_chars() and from_chars() are available. These each have the usual <charconv>-like behavior, known from C++17. For motivational words on to_chars() and from_chars(), see also issue 153 and issue 398.
Support for importing and exporting bits is granted by the subroutines import_bits() and export_bits(). Their interfaces, input/output forms and constraints are intended to be identical with those used in Boost's import/export-bits functions.
Alternative libraries for big integral types include, among others, most notably GMP and Boost.Multiprecision.
At the moment, the digit range of wide-integer is limited to the granularity of the full limb type. This means that less-common bit counts requiring the use of non-full limbs are not supported. It is not possible with this library, for instance, to synthesize, let's say, a 61-bit integral type.
This can have performance impact. If you would like to synthesize an 80-bit integral type, for example, this can be done, but at the cost of using five 16-bit limbs. This degrades performance due to the higher limb count. This phenomenon was discussed in issue 234
| Back | FazBrowse Home | New Git URL |