Arbitrary precision unsigned integer supporting just post increment operator - c++

I am looking for a very simple C++ class implementing an unsigned integer with arbitrary precision and just the post increment operator.
I know there are library for arbitrary precision integer arithmetic but my needs are quite simple and I prefer to avoid the weight of a full library.
It seems to me that my current implementation is still not simple enough and not elegant enough. What do you suggest?
#include <vector>
#include <string>
#include <algorithm>
class UNat
{
public:
static const char base = 10;
UNat( const char* n )
{
std::string s(n);
std::reverse(s.begin(),s.end());
for ( size_t i = 0; i < s.length(); i++ ) {
n_.push_back(s[i]-'0');
}
}
UNat& operator++(int)
{
bool carry = false;
bool finished = false;
for ( size_t i = 0; i < n_.size() && !finished; i++ ) {
n_[i] = add(n_[i],1,carry);
if ( carry ) {
finished = false;
} else {
finished = true;
}
}
if ( carry ) {
n_.push_back(1);
}
return *this;
}
std::string to_string() const
{
std::string r(n_.begin(), n_.end());
std::reverse(r.begin(),r.end());
std::for_each(r.begin(), r.end(), [](char& d) { d+='0';});
return r;
}
private:
char add( const char& a, const char& b, bool& carry )
{
char cc = a + b;
if ( cc >= base ) {
carry = true;
cc -= base;
} else {
carry = false;
}
return cc;
}
std::vector< char > n_;
};
std::ostream& operator<<(std::ostream& oss, const UNat& n)
{
oss << n.to_string();
return oss;
}
#include <iostream>
int main()
{
UNat n("0");
std::cout << n++ << "\n";
std::cout << UNat("9")++ << "\n";
std::cout << UNat("99")++ << "\n";
std::cout << UNat("19")++ << "\n";
std::cout << UNat("29")++ << "\n";
std::cout << UNat("39")++ << "\n";
return 0;
}

In order to avoid returning the mutated value, save a local copy and return it:
UNat operator++(int)
{
UNat copy = *this;
// ....
return copy;
} // don't return by reference
Comparatively, the prefix operator does return by reference.
UNat& operator++ ()
{
// ....
return *this;
}
Some tips and tricks from Arbitrary-precision arithmetic Explanation:
1/ When adding or multiplying numbers, pre-allocate the maximum space
needed then reduce later if you find it's too much. For example,
adding two 100-"digit" (where digit is an int) numbers will never give
you more than 101 digits. Multiply a 12-digit number by a 3 digit
number will never generate more than 15 digits (add the digit counts).
An alternative implementation for your addition function can look like this:
c->lastdigit = std::max(a->lastdigit, b->lastdigit)+1;
carry = 0;
for (i=0; i<=(c->lastdigit); i++) {
c->digits[i] = (char) (carry+a->digits[i]+b->digits[i]) % 10;
carry = (carry + a->digits[i] + b->digits[i]) / 10;
}

The code implements what's generally known as Binary Coded Decimal (BCD). Very simple, and, as you say, the implementation can be much simpler if you only want to increment and don't need general addition. To simplify even further, use the internal character representations for the digits '0' through '9' instead of the values 0 through 9. And for another simplification, store the characters in a std::string:
for (int index = 0; index < digits.size(); ++index) {
if (digits[index] != '9') {
++digits[index];
break;
}
digits[index] = '0';
}
if (index == digits.size())
digits.push_back('1');
This makes the stream inserter nearly trivial.

Related

C++ Homework - overloading >> operator with dynamic array

Intro:
I have a homework assignment to create a type for "ints larger than existing types can store."
We should store the numbers in an array of digits(backward for easier math logic).
I'm using a dynamic array to store the info in the object.
I am required to overload at least the +, -, * , <, >, << and >> operators.
Also .cpp and .h files must be separate.
Problem:
Not too sure how to overload the >> operator based on the class attributes and manipulation needed.
BigIntegers.h
#include <string>
#include <iostream>
typedef int* BigIntPtr;
class BigIntegers {
private:
int size; // based on string size, if neg string size -1
BigIntPtr number; // dynamic array ptr
bool isNeg; // set default to false, assumes a positive number
public:
explicit BigIntegers(std::string num = "");
BigIntegers(const BigIntegers &bi);
~BigIntegers();
friend std::istream &operator>>(std::istream &is, BigIntegers &bi) {
/**
* using eg "is >> bi.data;" doesn't seem viable given the data manipulation needed
* see constructor
*/
std::string input;
getline(is,input);
bi = BigIntegers(input);
return is;
}
friend std::ostream &operator<<(std::ostream &os, const BigIntegers &bi) {
if(bi.isNeg) //add sign if needed
os << '-';
for(int s=bi.size-1;s>-1;s--) //print reverse
{
os << bi.number[s];
}
return os;
}
};
BigIntegers.cpp
#include <algorithm>
#include "BigIntegers.h"
BigIntegers::BigIntegers(std::string num) {
//if null
if(num.empty())
{
size = 0;
number = NULL;
isNeg = 0;
return;
}
//determine if its negative
if (num.find('-') == 0)
{
num.erase(remove(num.begin(),num.end(), '-'),num.end());
isNeg =true;
}else {isNeg= false;}
size = num.length();
number = new int[size];
//add array backwards for math optimization
std::string rev; rev.assign(num.rbegin(),num.rend());
for(int i = 0; i < size; i++)
{
number[i]=rev[i]-'0';
}
}
BigIntegers::~BigIntegers() {
delete [] number;
size =0;
isNeg =0;
}
#include <iostream>
#include "BigIntegers.h"
using std::cout;
using std::cin;
using std::string;
int main() {
//basic functionality test
string stringInt = "123456";
string stringIntNeg = "-99987654321";
BigIntegers test1(stringInt);
cout << test1 << "\n";
BigIntegers test2(stringIntNeg);
cout << test2 << "\n";
//iostream test
cout << "Enter a big integer in the form 123456 or -123456.\n";
BigIntegers test3;
cin >> test3;
cout << test3 << "\n";
return 0;
}
output
pr4_bigIntegers\cmake-build-debug\pr4_bigIntegers.exe
123456
-12345678987654321
Enter a big integer in the form 123456 or -123456.
5789256
-57883070081-2144186072
Process finished with exit code 0
Note:
Additionally, sometimes the output is almost correct but negative or some other garbage values are included. eg) cin >> 5314 , cout <<-5314
edit - I've realized that after 4 digits the garbage is introduced. The experimentation continues.
Assignment Instructions - (for additional context, this is a direct copy/paste)
The existing types of integers in C++ cannot store very large integers. We need a new type that can store these large integers we possibly need in dealing with scientific problems.
You can represent an integer by storing the integer as an array of digits.
Design and implement a class for integer arithmetic in which a number is implemented as an array of digits. Each entry of the array will be a digit from 0 to 9 (inclusive).
The number represented is the concatenation of the digits in the array.
You are required to overload at least the +, -, * , <, >, << and >> operators for this class. Try to overload the division operator /.
Do not forget to implement the gang of three: assignment operator, copy constructor and destructor.
Your division operator is an integer operator so that it returns the integer part of the quotient. You need to understand that the purpose of this class is to store large integers so you should not convert your array representation into regular integer representation during the process of overloading these operators. Again, we assume that these integers cannot be handled by using the build-in integer types so your explicit constructor should have a string type parameter, not an integer type parameter, and get each character from the string, convert it to a digit and store it to your array. To perform operations easily, you may want to store an integer in reversed order in your array.
Use dynamic array to store your integer.
Include professional documentation of your code and proper indentation
Separate your header file from implementation file
Test every aspect of your class.
Email clarification from teacher
Just answer a couple of questions from some of you.
The integers are signed because when you do your subtraction you may get a negative integer. So use the first spot of the array to store 0 or 1 (0 for negative and 1 for positive).
The instructions do not allow you to convert the string parameter to an integer. I mean that you should not convert string s="123456" to int n=123456. But, you have to convert character 1 into integer 1, ..., character 6 into integer 6 and store each into your array.
Your overloaded >> operator seems to be correct.
Your second problem: The minus sign is no garbage.
bool isNeg; // set default to false, assumes a positive number
You never set it to false. I debugged your code, and the solution is simple:
BigIntegers::BigIntegers(std::string num) : isNeg(false) {
//your constructor stuff
}
I suggest using a dynamic array of type unsigned short instead.
Saving each digit in an integer each ranging from -2.14 Billion to 2.14 Billion is overkill will require a lot of memory. You don't need negative values to store. You may consider using chars, as you can convert each integer digit into a char and backward but less memory is required. The most memory-efficient way is perhaps an Enum ranging from 0 to 9.
Your class would require storing a number greater than a long long (8 bytes) at least 64 bytes (number array) + 4 bytes for the size variable + 1 bit (isNeg). This is quite large for a number :)
According to your task, you are not allowed to use integers. So you have to change it :)
My approach exploits the fact that each element of an enum class can be converted to an integer type according to its index in the enum class definition (and vice versa). Eventually, each integer can be converted to a char. Thus, there are still integers but you can spot them hardly.
enum class Digit{Zero,One,Two,Three,Four,Five,Six,Seven,Eight,Nine};
typedef Digit* BigIntPtr;
class BigIntegers
{
public:
explicit BigIntegers(std::string num = "");
BigIntegers(const BigIntegers& src);
BigIntegers& operator=(const BigIntegers&src);
~BigIntegers();
friend bool operator<(const BigIntegers& lhs, const BigIntegers& rhs);
friend bool operator>(const BigIntegers& lhs, const BigIntegers& rhs);
friend BigIntegers operator+(const BigIntegers& lhs, const BigIntegers& rhs);
friend BigIntegers operator-(const BigIntegers& lhs, const BigIntegers&rhs);
friend std::istream &operator>>(std::istream &is, BigIntegers &bi);
friend std::ostream &operator<<(std::ostream &os, const BigIntegers &bi);
private:
size_t size; // based on string size, if neg string size -1
BigIntPtr number; // dynamic array ptr
bool isNeg; // set default to false, assumes a positive number
};
Your .cpp file:
#include <algorithm>
#include "bigint.h"
using namespace std;
BigIntegers::BigIntegers(std::string num):isNeg(false)
{
//if null
if (num.empty())
{
size = 0;
number = NULL;
isNeg = 0;
return;
}
//determine if its negative
if (num.find('-') == 0)
{
num.erase(remove(num.begin(), num.end(), '-'), num.end());
isNeg = true;
}
size = num.length();
number = new Digit[size];
//add array backwards for math optimization
std::string rev; rev.assign(num.rbegin(), num.rend());
Digit * aux = number;
std::for_each (rev.begin(),rev.end(),[&](const char c)
{
*number = Digit(c - '0');
number++;
});
number = aux;
}
BigIntegers::BigIntegers(const BigIntegers & src)
: size(src.size), number{new Digit[src.size]}, isNeg(src.isNeg)
{
for (auto i = number, j = src.number; i < number + size; i++, j++)
{
*i = *j;
}
}
BigIntegers & BigIntegers::operator=(const BigIntegers & src)
{
if (this == &src)
return *this;
size = src.size;
isNeg = src.isNeg;
if (number != NULL) delete[] number;
number = new Digit[src.size];
for (auto i = number, j = src.number; i < number + size; i++,j++)
{
*i = *j;
}
return *this;
}
BigIntegers::~BigIntegers()
{
delete[] number;
}
bool operator<(const BigIntegers & lhs, const BigIntegers & rhs)
{
if (lhs.size > rhs.size) return false;
if (lhs.size < rhs.size) return true;
for (auto i = lhs.number + lhs.size - 1, j = rhs.number + rhs.size - 1; i >= lhs.number || j >= rhs.number; i--, j--)
{
if (char(*i) > char(*j))return false;
if (char(*i) < char(*j))return true;
}
return false;
}
bool operator>(const BigIntegers & lhs, const BigIntegers & rhs)
{
return !(lhs < rhs);
}
BigIntegers operator+(const BigIntegers & lhs, const BigIntegers & rhs)
{
string value = "";
Digit aux = Digit::Zero;
for (auto i = lhs.number, j = rhs.number; i < lhs.number + lhs.size || j < rhs.number + rhs.size; i++, j++)
{
char c = char(aux);
c += i < lhs.number + lhs.size ? char(*i) : char(0);
c += j < rhs.number + rhs.size ? char(*j) : char(0);
aux = Digit(0);
if (c > 9)
{
aux = Digit::One;
c -= 10;
}
// 48 is '0' in Ascii table
value += (c+48);
}
if (aux == Digit::One)
{
value += '1';
}
reverse(value.begin(), value.end());
return BigIntegers(value);
}
BigIntegers operator-(const BigIntegers & lhs, const BigIntegers & rhs)
{
bool reverse = false;
if (lhs < rhs)reverse = true;
const BigIntegers& bigger = reverse ? rhs : lhs;
const BigIntegers& smaller = reverse ? lhs : rhs;
Digit aux = Digit::Zero;
std::string value = "";
for (auto i = bigger.number, j = smaller.number; i < bigger.number+bigger.size; i++, j++)
{
char c1 = char(*i);
char c2 = j < smaller.number+smaller.size ? char(*j) : 0;
c2 += char(aux);
aux = Digit::Zero;
if (c1 < c2)
{
aux = Digit::One;
c1 = c1 + 10 - c2;
}
else
{
c1 -= c2;
}
if (c1 > 0 || i < bigger.number + bigger.size - 1)
{
// if condition is to avoid leading zeros
value += (c1 + 48);
}
}
if (reverse)value += "-";
std::reverse(value.begin(), value.end());
return BigIntegers(value);
}
istream& operator>>(istream& is, BigIntegers& bi)
{
std::string input;
getline(is, input);
bi = BigIntegers(input);
return is;
}
std::ostream &operator<<(std::ostream &os, const BigIntegers &bi) {
if (bi.isNeg) //add sign if needed
os << '-';
for (int s = bi.size - 1; s > -1; s--) //print reverse
{
os << static_cast<int>(bi.number[s]);
}
return os;
}
As you may notice, I replaced all for loops using an integer ;)
At least I am not using the int keyword once. However, something like +10 is of course a const integer.
Up to this point, I have not yet been able to determine that storing the digits in reverse is advantageous.
The multiplication is up to you. Nice task :)
This overload is what solved the problem of the garbage values.
BigIntegers &BigIntegers::operator=(const BigIntegers &bi) {
//if number is already assigned here
if(this==&bi)
return *this;
//else assign the number
size = bi.size; isNeg = bi.isNeg;
if (number != nullptr) delete[] number;
number= new int[bi.size];
for (auto i = number, j = bi.number; i < number+size; i++, j++)
*i = *j;
return *this;
}

Formatting Commas into a long long integer

this is my first time posting a question. I was hoping to get some help on a very old computer science assignment that I never got around to finishing. I'm no longer taking the class, just want to see how to solve this.
Read in an integer (any valid 64-bit
integer = long long type) and output the same number but with commas inserted.
If the user entered -1234567890, your program should output -1,234,567,890. Commas
should appear after every three significant digits (provided more digits remain) starting
from the decimal point and working left toward more significant digits. If the number
entered does not require commas, do not add any. For example, if the input is 234 you
should output 234. The input 0 should produce output 0. Note in the example above
that the number can be positive or negative. Your output must maintain the case of the
input.
I'm relatively new to programming, and this was all I could come up with:
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
long long n;
cout << "Enter an integer:" << endl;
cin >> n;
int ones = n % 10;
int tens = n / 10 % 10;
int hund = n / 100 % 10;
int thous = n / 1000 % 10;
int tthous = n / 10000 % 10;
cout << tthous << thous << "," << hund << tens << ones << endl;
return 0;
}
The original assignment prohibited the use of strings, arrays, and vectors, so please refrain from giving suggestions/solutions that involve these.
I'm aware that some sort of for-loop would probably be required to properly insert the commas in the necessary places, but I just do not know how to go about implementing this.
Thank you in advance to anyone who offers their help!
Just to give you an idea how to solve this, I've maiden a simple implementation. Just keep in mind that is just a simple example:
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
long long n = -1234567890;
if ( n < 0 )
cout << '-';
n = abs(n);
for (long long i = 1000000000000; i > 0; i /= 1000) {
if ( n / i <= 0 ) continue;
cout << n / i ;
n = n - ( n / i) * i;
if ( n > 0 )
cout << ',';
}
return 0;
}
http://coliru.stacked-crooked.com/a/150f75db89c46e99
The easy solution would be to use ios::imbue to set a locale that would do all the work for you:
std::cout.imbue(std::locale(""));
std::cout << n << std::endl;
However, if the restraints don't allow for strings or vectors I doubt that this would be a valid solution. Instead you could use recursion:
void print(long long n, int counter) {
if (n > 0) {
print(n / 10, ++counter);
if (counter % 3 == 0) {
std::cout << ",";
}
std::cout << n%10;
}
}
void print(long long n) {
if (n < 0) {
std::cout << "-";
n *= -1;
}
print(n, 0);
}
And then in the main simply call print(n);
A small template class comma_sep may be a solution, the usage may be as simple as:
cout << comma_sep<long long>(7497592752850).sep() << endl;
Which outputs:
7,497,592,752,850
Picked from here:
https://github.com/arloan/libimsux/blob/main/comma_sep.hxx
template <class I = int, int maxdigits = 32>
class comma_sep
char buff[maxdigits + maxdigits / 3 + 2];
char * p;
I i;
char sc;
public:
comma_sep(I i, char c = ',') : p(buff), i(i), sc(c) {
if (i < 0) {
buff[0] = '-';
*++p = '\0';
}
}
const char * sep() {
return _sep(std::abs(i));
}
private:
const char * _sep(I i) {
I r = i % 1000;
I n = i / 1000;
if (n > 0) {
_sep(n);
p += sprintf(p, "%c%03d", sc, (int)r);
*p = '\0';
} else {
p += sprintf(p, "%d", (int)r);
*p = '\0';
}
return buff;
}
};
The above class handles only integeral numbers, float/double numbers need to use a partial specialized version:
template<int maxd>
class comma_sep<double, maxd> {
comma_sep<int64_t, maxd> _cs;
char fs[64];
double f;
public:
const int max_frac = 12;
comma_sep(double d, char c = ',') : _cs((int64_t)d, c) {
double np;
f = std::abs(modf(d, &np));
}
const char * sep(int frac = 3) {
if (frac < 1 || frac > max_frac) {
throw std::invalid_argument("factional part too too long or invalid");
}
auto p = _cs.sep();
strcpy(fs, p);
char fmt[8], tmp[max_frac+3];
sprintf(fmt, "%%.%dlf", frac);
sprintf(tmp, fmt, f);
return strcat(fs, tmp + 1);
}
};
The two above classes can be improved by adding type-traits like std::is_integral and/or std::is_floating_point, though.

hex string arithmetic in c++

I want to do basic arithmetic (addition, subtraction and comparison) with 64 digit hex numbers represented as strings. for example
"ffffa"+"2" == "ffffc"
Since binary representation of such a number requires 256 bits, I cannot convert the string to basic integer types. one solution is to use gmp or boost/xint but they are too big for this simple functionality.
Is there a lightweight solution that can help me?
Just write a library which will handle the strings with conversion between hex to int and will add one char at a time, taking care of overflow. It took minutes to implement such an algorithm:
#include <cstdio>
#include <sstream>
#include <iostream>
using namespace std;
namespace hexstr {
char int_to_hexchar(int v) {
if (0 <= v && v <= 9) {
return v + '0';
} else {
return v - 10 + 'a';
}
}
int hexchar_to_int(char c) {
if ('0' <= c && c <= '9') {
return c - '0';
} else {
return c - 'a' + 10;
}
}
int add_digit(char a, char b) {
return hexchar_to_int(a) + hexchar_to_int(b);
}
void reverseStr(string& str) {
int n = str.length();
for (int i = 0; i < n / 2; i++)
swap(str[i], str[n - i - 1]);
}
void _add_val_to_string(string& s, int& val) {
s.push_back(int_to_hexchar(val % 16));
val /= 16;
}
string add(string a, string b)
{
auto ita = a.end();
auto itb = b.end();
int tmp = 0;
string ret;
while (ita != a.begin() && itb != b.begin()) {
tmp += add_digit(*--ita, *--itb);
_add_val_to_string(ret, tmp);
}
while (ita != a.begin()) {
tmp += hexchar_to_int(*--ita);
_add_val_to_string(ret, tmp);
}
while (itb != b.begin()) {
tmp += hexchar_to_int(*--itb);
_add_val_to_string(ret, tmp);
}
while (tmp) {
_add_val_to_string(ret, tmp);
}
reverseStr(ret);
return ret;
}
}
int main()
{
std::cout
<< "1bd5adead01230ffffc" << endl
<< hexstr::add(
std::string() + "dead0000" + "00000" + "ffffa",
std::string() + "deaddead" + "01230" + "00002"
) << endl;
return 0;
}
This can be optimized, the reversing string maybe can be omitted and some cpu cycles and memory allocations spared. Also error handling is lacking. It will work only on implementations that use ASCII table as the character set and so on... But it's as simple as that. I guess this small lib can handle any hex strings way over 64 digits, depending only on the host memory.
Implementing addition, subtraction and comparison over fixed-base numeric strings yourself should be quite easy.
For instance, for addition and subtraction, simply do it as you would in paper: start on the right-hand end of both strings, parse the chars, compute the result, then carry over, etc. Comparison is even easier, and you go left-to-right.
Of course, all this is assuming you don't need performance (otherwise you should be using a proper library).

How to convert Biginteger to string

I have a vector with digits of number, vector represents big integer in system with base 2^32. For example:
vector <unsigned> vec = {453860625, 469837947, 3503557200, 40}
This vector represent this big integer:
base = 2 ^ 32
3233755723588593872632005090577 = 40 * base ^ 3 + 3503557200 * base ^ 2 + 469837947 * base + 453860625
How to get this decimal representation in string?
Here is an inefficient way to do what you want, get a decimal string from a vector of word values representing an integer of arbitrary size.
I would have preferred to implement this as a class, for better encapsulation and so math operators could be added, but to better comply with the question, this is just a bunch of free functions for manipulating std::vector<unsigned> objects. This does use a typedef BiType as an alias for std::vector<unsigned> however.
Functions for doing the binary division make up most of this code. Much of it duplicates what can be done with std::bitset, but for bitsets of arbitrary size, as vectors of unsigned words. If you want to improve efficiency, plug in a division algorithm which does per-word operations, instead of per-bit. Also, the division code is general-purpose, when it is only ever used to divide by 10, so you could replace it with special-purpose division code.
The code generally assumes a vector of unsigned words and also that the base is the maximum unsigned value, plus one. I left a comment wherever things would go wrong for smaller bases or bases which are not a power of 2 (binary division requires base to be a power of 2).
Also, I only tested for 1 case, the one you gave in the OP -- and this is new, unverified code, so you might want to do some more testing. If you find a problem case, I'll be happy to fix the bug here.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
namespace bigint {
using BiType = std::vector<unsigned>;
// cmp compares a with b, returning 1:a>b, 0:a==b, -1:a<b
int cmp(const BiType& a, const BiType& b) {
const auto max_size = std::max(a.size(), b.size());
for(auto i=max_size-1; i+1; --i) {
const auto wa = i < a.size() ? a[i] : 0;
const auto wb = i < b.size() ? b[i] : 0;
if(wa != wb) { return wa > wb ? 1 : -1; }
}
return 0;
}
bool is_zero(BiType& bi) {
for(auto w : bi) { if(w) return false; }
return true;
}
// canonize removes leading zero words
void canonize(BiType& bi) {
const auto size = bi.size();
if(!size || bi[size-1]) return;
for(auto i=size-2; i+1; --i) {
if(bi[i]) {
bi.resize(i + 1);
return;
}
}
bi.clear();
}
// subfrom subtracts b from a, modifying a
// a >= b must be guaranteed by caller
void subfrom(BiType& a, const BiType& b) {
unsigned borrow = 0;
for(std::size_t i=0; i<b.size(); ++i) {
if(b[i] || borrow) {
// TODO: handle error if i >= a.size()
const auto w = a[i] - b[i] - borrow;
// this relies on the automatic w = w (mod base),
// assuming unsigned max is base-1
// if this is not the case, w must be set to w % base here
borrow = w >= a[i];
a[i] = w;
}
}
for(auto i=b.size(); borrow; ++i) {
// TODO: handle error if i >= a.size()
borrow = !a[i];
--a[i];
// a[i] must be set modulo base here too
// (this is automatic when base is unsigned max + 1)
}
}
// binary division and its helpers: these require base to be a power of 2
// hi_bit_set is base/2
// the definition assumes CHAR_BIT == 8
const auto hi_bit_set = unsigned(1) << (sizeof(unsigned) * 8 - 1);
// shift_right_1 divides bi by 2, truncating any fraction
void shift_right_1(BiType& bi) {
unsigned carry = 0;
for(auto i=bi.size()-1; i+1; --i) {
const auto next_carry = (bi[i] & 1) ? hi_bit_set : 0;
bi[i] >>= 1;
bi[i] |= carry;
carry = next_carry;
}
// if carry is nonzero here, 1/2 was truncated from the result
canonize(bi);
}
// shift_left_1 multiplies bi by 2
void shift_left_1(BiType& bi) {
unsigned carry = 0;
for(std::size_t i=0; i<bi.size(); ++i) {
const unsigned next_carry = !!(bi[i] & hi_bit_set);
bi[i] <<= 1; // assumes high bit is lost, i.e. base is unsigned max + 1
bi[i] |= carry;
carry = next_carry;
}
if(carry) { bi.push_back(1); }
}
// sets an indexed bit in bi, growing the vector when required
void set_bit_at(BiType& bi, std::size_t index, bool set=true) {
std::size_t widx = index / (sizeof(unsigned) * 8);
std::size_t bidx = index % (sizeof(unsigned) * 8);
if(bi.size() < widx + 1) { bi.resize(widx + 1); }
if(set) { bi[widx] |= unsigned(1) << bidx; }
else { bi[widx] &= ~(unsigned(1) << bidx); }
}
// divide divides n by d, returning the result and leaving the remainder in n
// this is implemented using binary division
BiType divide(BiType& n, BiType d) {
if(is_zero(d)) {
// TODO: handle divide by zero
return {};
}
std::size_t shift = 0;
while(cmp(n, d) == 1) {
shift_left_1(d);
++shift;
}
BiType result;
do {
if(cmp(n, d) >= 0) {
set_bit_at(result, shift);
subfrom(n, d);
}
shift_right_1(d);
} while(shift--);
canonize(result);
canonize(n);
return result;
}
std::string get_decimal(BiType bi) {
std::string dec_string;
// repeat division by 10, using the remainder as a decimal digit
// this will build a string with digits in reverse order, so
// before returning, it will be reversed to correct this.
do {
const auto next_bi = divide(bi, {10});
const char digit_value = static_cast<char>(bi.size() ? bi[0] : 0);
dec_string.push_back('0' + digit_value);
bi = next_bi;
} while(!is_zero(bi));
std::reverse(dec_string.begin(), dec_string.end());
return dec_string;
}
}
int main() {
bigint::BiType my_big_int = {453860625, 469837947, 3503557200, 40};
auto dec_string = bigint::get_decimal(my_big_int);
std::cout << dec_string << '\n';
}
Output:
3233755723588593872632005090577

Converting many bits to Base 10

I am building a class in C++ which can be used to store arbitrarily large integers. I am storing them as binary in a vector. I need to be able to print this vector in base 10 so it is easier for a human to understand. I know that I could convert it to an int and then output that int. However, my numbers will be much larger than any primitive types. How can I convert this directly to a string.
Here is my code so far. I am new to C++ so if you have any other suggestions that would be great too. I need help filling in the string toBaseTenString() function.
class BinaryInt
{
private:
bool lastDataUser = true;
vector<bool> * data;
BinaryInt(vector<bool> * pointer)
{
data = pointer;
}
public:
BinaryInt(int n)
{
data = new vector<bool>();
while(n > 0)
{
data->push_back(n % 2);
n = n >> 1;
}
}
BinaryInt(const BinaryInt & from)
{
from.lastDataUser = false;
this->data = from.data;
}
~BinaryInt()
{
if(lastDataUser)
delete data;
}
string toBinaryString();
string toBaseTenString();
static BinaryInt add(BinaryInt a, BinaryInt b);
static BinaryInt mult(BinaryInt a, BinaryInt b);
};
BinaryInt BinaryInt::add(BinaryInt a, BinaryInt b)
{
int aSize = a.data->size();
int bSize = b.data->size();
int newDataSize = max(aSize, bSize);
vector<bool> * newData = new vector<bool>(newDataSize);
bool carry = 0;
for(int i = 0; i < newDataSize; i++)
{
int sum = (i < aSize ? a.data->at(i) : 0) + (i < bSize ? b.data->at(i) : 0) + carry;
(*newData)[i] = sum % 2;
carry = sum >> 1;
}
if(carry)
newData->push_back(carry);
return BinaryInt(newData);
}
string BinaryInt::toBinaryString()
{
stringstream ss;
for(int i = data->size() - 1; i >= 0; i--)
{
ss << (*data)[i];
}
return ss.str();
}
string BinaryInt::toBaseTenString()
{
//Not sure how to do this
}
I know you said in your OP that "my numbers will be much larger than any primitive types", but just hear me out on this.
In the past, I've used std::bitset to work with binary representations of numbers and converting back and forth from various other representations. std::bitset is basically a fancy std::vector with some added functionality. You can read more about it here if it sounds interesting, but here's some small stupid example code to show you how it could work:
std::bitset<8> myByte;
myByte |= 1; // mByte = 00000001
myByte <<= 4; // mByte = 00010000
myByte |= 1; // mByte = 00010001
std::cout << myByte.to_string() << '\n'; // Outputs '00010001'
std::cout << myByte.to_ullong() << '\n'; // Outputs '17'
You can access the bitset by standard array notation as well. By the way, that second conversion I showed (to_ullong) converts to an unsigned long long, which I believe has a max value of 18,446,744,073,709,551,615. If you need larger values than that, good luck!
Just iterate (backwards) your vector<bool> and accumulate the corresponding value when the iterator is true:
int base10(const std::vector<bool> &value)
{
int result = 0;
int bit = 1;
for (vb::const_reverse_iterator b = value.rbegin(), e = value.rend(); b != e; ++b, bit <<= 1)
result += (*b ? bit : 0);
return result;
}
Live demo.
Beware! this code is only a guide, you will need to take care of int overflowing if the value is pretty big.
Hope it helps.