4 Şubat 2020 Salı

multiprecision cpp_int Sınıfı

Giriş
128 bitten 1024 bit büyüklüğüne kadar bir sürü hazır tanımlı sınıf mevcut. Bu hazır sınıflar ile cpp_int için yapılabilen tüm işlemler yapılabilir. Bu sınıfların için nasıl olduğunu görmek için örnek bir proje burada.
#include <boost/multiprecision/cpp_int.hpp>

using namespace boost::multiprecision;

int128_t   mySignedInt128    = -1;
uint128_t  myUnsignedInt128  =  2;
int256_t   mySignedInt256    = -3;
uint256_t  myUnsignedInt256  =  4;
int512_t   mySignedInt512    = -5;
uint512_t  myUnsignedInt512  =  6;
int1024_t  mySignedInt1024   = -7;
uint1024_t myUnsignedInt1024 =  8;
cpp_int Sınıfı - Arbitrary precision integer
Şu satırı dahil ederiz. mpz_int ile kardeştir.
#include <boost/multiprecision/cpp_int.hpp>    
Kolay kullanmak için şu satırı dahil ederiz.
namespace mp = boost::multiprecision;
Kolay kullanmak için şu satırı dahil ederiz.
using namespace boost::multiprecision;
Bu sınıf aslında bir typedef. Dolayısıyla number sınıfına da bakılabilir.
typdef number<cpp_int_backend<>> cpp_int;
Bu sınıf altta std::unary_function template'ını kullanıyor. C++17 ile bu template kaldırıldı. Bazı derleme hataları oluşabilir. Açıklaması şöyle
Some templates in boost::multiprecision use std::unary_function, which has been deprecated since C++11 and was removed from the standard for C++17.
Constructor - int
Şöyle yaparız.
mp::cpp_int bigint = 113850000000;
Şöyle yaparız.
mp::cpp_int bigint (1); 
Constructor - string
Şöyle yaparız.
mp::cpp_int bigint ("11111111112222222222333333333344444444445555555555");
backend  metodu
Depolama belleğini döndürür.
Örnek
Şöyle yaparız.
std::size_t size = bingint.backend().size();
mp::limb_type* p = bingint.backend().limbs();
Örnek
Şöyle yaparız.
std::copy_n(bigint.backend().limbs(), 
            bigint.backend().size(), 
            std::ostream_iterator<unsigned>(
              std::cout << std::hex << std::showbase, " "
            )
);
Örnek
Şöyle yaparız.
template <typename T> struct hash_bin {
  size_t operator()(const T &t) const {
    return boost::hash_range(t.backend().limbs(), t.backend().limbs()+t.backend().size());
  }
};

using bmp::cpp_int;
boost::unordered_map<cpp_int, cpp_int, hash_bin<cpp_int> > mp;
convert_to metodu
Sayıyı belirtilen tipe çevirir.
Örnek - int
Şöyle yaparız.
boost::multiprecision::cpp_int val{ 5 };
val *= 5;
val *= 5;
int i = val.convert_to<int>();
Örnek - double
Şöyle yaparız
int256_t x("12345678901234567890");

std::cout << "log2(" << x << ") = " << log2(x.convert_to<double>()) << "\n";
Örnek
Şöyle yaparız.
using bigint   = boost::multiprecision::cpp_int;
using bigfloat = boost::multiprecision::cpp_dec_float_50;


bigint x(12345678); 
bigfloat y("0.26");
std::cout << "x: " << x << "\n";
std::cout << "y: " << y << "\n";
bigfloat result = x.convert_to<bigfloat>() * y; //3.209.876,28

//bigint z = result; // lossy conversion will not compile
bigint z1 = static_cast<bigint>(result); //Explicit loss
bigint z2 = result.convert_to<bigint>(); //Explicit loss

std::cout << "Result: " << result << "\n";
std::cout << "z1: " << z1 << "\n";
std::cout << "z2: " << z2 << "\n";
Çıktı olarak şunu alırız.
x: 12345678
y: 0.26
Result: 3.20988e+06
z1: 3209876
z2: 3209876
is_zero metodu
Şöyle yaparız.
unsigned ffs512(const boost::multiprecision::uint512_t &n) {
  if (n.is_zero()) {
    return 0;
  } else {
    return boost::multiprecision::lsb(n) + 1;
  }
}
operator *= metodu
Şöyle yaparız.
unsigned i = 100;
mp::bigint *= i;
operator < metodu
Şöyle yaparız.
int128_t a = ...;
int128_t b = ...;

std::cout << std::max(a, b) << std::endl;
operator << metodu - Bitshift
Şöyle yaparız.
#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;

int128_t int64left = CreateRandomInt64ViaBoost()
int128_t int64right = CreateRandomInt64ViaBoost()

int128_t randomInt = int64left << 64 | int64right;
operator << metodu - Stream
Sayı stream'e yazılabilir. Şöyle yaparız.
std::cout << bigint << std::endl;
Şöyle yaparız
std::ostringstream os;
os << std::hex << bigint; 
Free Style Metodlar
sizeof 
Açıklaması şöyle. Yani 128 mutlaka16 byte büyüklüğünde olmalı diye düşünmemek lazım.
When used at fixed precision, the size of this type is always one machine word larger than you would expect for an N-bit integer: the extra word stores both the sign, and how many machine words in the integer are actually in use. The latter is an optimisation for larger fixed precision integers, so that a 1024-bit integer has almost the same performance characteristics as a 128-bit integer, rather than being 4 times slower for addition and 16 times slower for multiplication (assuming the values involved would always fit in 128 bits). Typically this means you can use an integer type wide enough for the "worst case scenario" with only minor performance degradation even if most of the time the arithmetic could in fact be done with a narrower type.
Şöyle yaparız. Çıktı olarak 16 yerine 24 alırız.
#include "boost/multiprecision/cpp_int.hpp"
using boost::multiprecision::uint128_t;

...

cout << sizeof(uint128_t);