I am working on a LargeInteger
class to store Integers beyond the size of long long
as a dynamic character array. I have overloaded most of the relevant operators to be used with the large integers.
I would like to find a way to further streamline the addition/subtraction. I think it ought to be possible to only use a single loop and manipulate the signs and order to produce the proper result, but all attempts thus far have failed.
The only things I am unwilling to change:
- Storing the data as a dynamic char array
- The attributes of the operators (friend, overloaded, return type, parameters)
This does not use proper C++ components such as std::vector
, std::copy
and others. My goals in this project were to avoid the use of the standard (or other) library aside from I/O.
Everything else is fair game, and I welcome any and all criticisms. This is one of the first C++ classes I have made, so I expect that my coding is sub-par.
LargeInt.h
#ifndef LARGEINT_H
#define LARGEINT_H
#include <iostream>
typedef unsigned int Counter;
typedef char Digit;
typedef unsigned int Index;
typedef bool Sign;
const bool kSignPositive = 0;
const bool kSignNegative = 1;
const bool kCarryTrue = 1;
const bool kCarryFalse = 0;
const bool kBorrowTrue = 1;
const bool kBorrowFalse = 0;
const Counter kDefaultCounterValue = 0;
const Digit kDefaultDigitValue = 0;
const Digit kMinDigitValue = 0;
const Digit kMaxDigitValue = 9;
const Digit kRadix = 10;
const Index kFirstIndex = 0;
const size_t kByteSize = 8;
const size_t kMinLength = 1;
namespace helper_templates
{
// Count Digits
template <typename T>
Counter TCountDigits(T x);
// Get Sign
template <typename T>
bool TGetSign(T x);
}
class LargeInt
{
private:
Sign sign_;
size_t length_;
Digit* data_;
public:
/*
* Constructors
*/
// Default
LargeInt();
// Copy
LargeInt(const LargeInt& x);
// Copy and Resize
LargeInt(const Sign sign, const size_t length, const Digit* data);
// Integer Type
LargeInt(const long long x);
/*
* Destructor
*/
~LargeInt();
/*
* Operators
*/
// Assignemnt
LargeInt& operator=(const LargeInt& x);
// Negation
LargeInt operator-();
// Addition
friend LargeInt operator+(LargeInt x, const LargeInt& y);
// Addition Assignment
LargeInt& operator+=(const LargeInt& x);
// Subtraction
friend LargeInt operator-(LargeInt x, const LargeInt& y);
// Subtraction Assignment
LargeInt& operator-=(const LargeInt& x);
// Preincrement
LargeInt& operator++();
// Postincrement
LargeInt operator++(int);
// Predecrement
LargeInt& operator--();
// Postdecrement
LargeInt operator--(int);
// Not
bool operator!();
// Equality
friend bool operator==(const LargeInt& x, const LargeInt& y);
// Inequality
friend bool operator!=(const LargeInt& x, const LargeInt& y);
// Greater Than
friend bool operator>(const LargeInt& x, const LargeInt& y);
// Greater Than or Equal To
friend bool operator>=(const LargeInt& x, const LargeInt& y);
// Less Than
friend bool operator<(const LargeInt& x, const LargeInt& y);
// Less Than or Equal To
friend bool operator <=(const LargeInt& x, const LargeInt& y);
// Insertion
friend std::ostream& operator<<(std::ostream& out,
const LargeInt& x);
/*
* Functions
*/
// Absolute Value
LargeInt Abs() const;
};
#endif
LargeInt.C
#include "LargeInt.h"
// Count Digits
template <typename T>
Counter helper_templates::TCountDigits(T x)
{
Counter c = kDefaultCounterValue;
while(x)
{
x /= kRadix;
c++;
}
return c;
}
// Get Sign
template <typename T>
Sign helper_templates::TGetSign(T x)
{
long long mask = 0;
mask |= 1 << ((sizeof(x) * kByteSize) - 1);
return (mask & x) >> ((sizeof(x) * kByteSize) - 1);
}
/*
* Constructors
*/
// Default
LargeInt::LargeInt()
: sign_(kSignPositive), length_(kMinLength), data_(new Digit[kMinLength])
{
data_[kFirstIndex] = kDefaultDigitValue;
}
// Copy
LargeInt::LargeInt(const LargeInt& x)
: sign_(x.sign_), length_(x.length_), data_(new Digit[x.length_])
{
for(Index i = kFirstIndex; i < length_; i++)
{
data_[i] = x.data_[i];
}
}
// Copy and Resize
LargeInt::LargeInt(const Sign sign, const size_t length, const Digit* data)
: sign_(sign), length_(length), data_(new Digit[length_])
{
for(Index i = kFirstIndex; i < length_; i++)
{
data_[i] = data[i];
}
}
// Integer Type
LargeInt::LargeInt(long long x)
: sign_(helper_templates::TGetSign(x)),
length_(helper_templates::TCountDigits(x)),
data_(new Digit[length_])
{
if(sign_ == kSignNegative)
{
x = -x;
}
for(Index i = kFirstIndex; i < length_; i++)
{
Digit d = x % kRadix;
data_[i] = d;
x /= kRadix;
}
}
/*
* Destructor
*/
LargeInt::~LargeInt()
{
delete [] data_;
}
/*
* Operators
*/
// Assignment
LargeInt& LargeInt::operator=(const LargeInt& x)
{
if(this != &x)
{
this->sign_ = x.sign_;
this->length_ = x.length_;
for(Index i = kFirstIndex; i < this->length_; i++)
{
this->data_[i] = x.data_[i];
}
}
return *this;
}
// Negation
LargeInt LargeInt::operator-()
{
return LargeInt(!this->sign_, this->length_, this->data_);
}
// Addition
LargeInt operator+(LargeInt x, const LargeInt& y)
{
return x += y;
}
// Addition Assignment
LargeInt& LargeInt::operator+=(const LargeInt& x)
{
LargeInt sum;
Digit d;
Digit carry = kCarryFalse;
Index i;
if(this->sign_ == kSignPositive && x.sign_ == kSignPositive)
{
if(*this >= x)
{
sum.length_ = this->length_ + kMinLength;
for(i = kFirstIndex; i < x.length_; i++)
{
d = this->data_[i] + x.data_[i] + carry;
if(d > kMaxDigitValue)
{
d %= kRadix;
carry = kCarryTrue;
}
else
{
carry = kCarryFalse;
}
sum.data_[i] = d;
}
while(i < this->length_)
{
d = this->data_[i] + carry;
if(d > kMaxDigitValue)
{
d %= kRadix;
carry = kCarryTrue;
}
else
{
carry = kCarryFalse;
}
sum.data_[i] = d;
i++;
}
if(!carry)
{
*this = LargeInt(sum.sign_, sum.length_ - kMinLength,
sum.data_);
}
else
{
*this = sum;
}
}
else
{
*this = x + *this;
}
}
else if(this->sign_ == kSignPositive)
{
*this = *this - x;
}
else if(x.sign_ == kSignPositive)
{
*this = x - *this;
}
else
{
*this = -(x.Abs() + this->Abs());
}
return *this;
}
// Subtraction
LargeInt operator-(LargeInt x, const LargeInt& y)
{
return x -= y;
}
// Subtraction Assignment
LargeInt& LargeInt::operator-=(const LargeInt& x)
{
LargeInt difference;
Digit d;
Digit borrow = kBorrowFalse;
Index i;
if(x.sign_ == kSignNegative)
{
*this = *this + x.Abs();
}
else if(this->sign_ == kSignNegative)
{
*this = -(this->Abs() + x);
}
else
{
if(*this > x)
{
difference.length_ = this->length_;
for(i = kFirstIndex; i < x.length_; i++)
{
d = this->data_[i] - x.data_[i] - borrow;
if(d < kDefaultDigitValue)
{
d += kRadix;
borrow = kBorrowTrue;
}
else
{
borrow = kBorrowFalse;
}
difference.data_[i] = d;
}
while(i < this->length_)
{
d = this->data_[i] - borrow;
if(d < kDefaultDigitValue)
{
d += kRadix;
borrow = kBorrowTrue;
}
else
{
borrow = kBorrowFalse;
}
difference.data_[i] = d;
i++;
}
for(i = difference.length_; i > kFirstIndex; i--)
{
if(difference.data_[i - 1] == kDefaultDigitValue)
{
difference.length_--;
}
else
{
break;
}
}
*this = LargeInt(kSignPositive, difference.length_,
difference.data_);
}
else if(x > *this)
{
*this = -(x - *this);
}
else
{
*this = LargeInt();
}
}
return *this;
}
// Preincrement
LargeInt& LargeInt::operator++()
{
*this += 1;
return *this;
}
// Postincrement
LargeInt LargeInt::operator++(int)
{
LargeInt temp(*this);
++*this;
return temp;
}
// Predecrement
LargeInt& LargeInt::operator--()
{
*this -= 1;
return *this;
}
// Postdecrement
LargeInt LargeInt::operator--(int)
{
LargeInt temp(*this);
--*this;
return temp;
}
// Not
bool LargeInt::operator!()
{
bool isNot;
if(*this == LargeInt())
{
isNot = true;
}
else
{
isNot = false;
}
return isNot;
}
// Equality
bool operator==(const LargeInt& x, const LargeInt& y)
{
bool isEqual;
if(x.sign_ != y.sign_)
{
isEqual = false;
}
else if(x.length_ != y.length_)
{
isEqual = false;
}
else
{
for(Index i = kFirstIndex; i < x.length_; i++)
{
if(x.data_[i] != y.data_[i])
{
isEqual = false;
break;
}
isEqual = true;
}
}
return isEqual;
}
// Inequality
bool operator!=(const LargeInt& x, const LargeInt& y)
{
return !(x == y);
}
// Greater Than
bool operator>(const LargeInt& x, const LargeInt& y)
{
bool isGreater;
if(x.sign_ == kSignPositive && y.sign_ == kSignPositive)
{
if(x.length_ == y.length_)
{
for(Index i = x.length_; i > kFirstIndex; i--)
{
if(x.data_[i - i] > y.data_[i - 1])
{
isGreater = true;
break;
}
else
{
isGreater = false;
}
}
}
else if(x.length_ > y.length_)
{
isGreater = true;
}
else
{
isGreater = false;
}
}
else if(x.sign_ == kSignPositive)
{
isGreater = true;
}
else if(y.sign_ == kSignPositive)
{
isGreater = false;
}
else
{
isGreater = y.Abs() > x.Abs();
}
return isGreater;
}
// Greater Than or Equal To
bool operator>=(const LargeInt& x, const LargeInt& y)
{
return (x > y) || (x == y);
}
// Less Than
bool operator<(const LargeInt& x, const LargeInt& y)
{
return y > x;
}
// Less Than or Equal To
bool operator <=(const LargeInt& x, const LargeInt& y)
{
return (x < y) || (x == y);
}
// Insertion
std::ostream& operator<<(std::ostream& out, const LargeInt& x)
{
if(x.sign_ == kSignNegative)
{
out << '-';
}
for(Index i = x.length_; i > kFirstIndex; i--)
{
out << +x.data_[i - 1];
}
return out;
}
/*
* Functions
*/
// Absolute Value
LargeInt LargeInt::Abs() const
{
return LargeInt(kSignPositive, this->length_, this->data_);
}
kRadix == 10
and not256
(or128
if you want to add digits without overflow)? You are wasting a lot of memory. \$\endgroup\$Storing the data as a dynamic char array.
: Well there is your first problem. This breaks the principle of separation of concerns. Your class should be responsible for resource management or business logic not both. \$\endgroup\$