Reduce memory footprint in the construction of a BigInt class











up vote
-1
down vote

favorite












I'm working on a concept at the moment and I'm writing the Pseudo code as I am asking this question. I'm thinking of making a fairly easy and simple to use class interface to represent BigInts. I'm thinking of making a couple of simple structures with basic properties and members that the BigInt class would use. For example instead of the BigInt class handling negative values directly it would contain a Sign struct and this struct would basically contain either a value of 0 or 1, or basically a Boolean type to designate if this BigInt is positive or negative. Upon Construction I intend to have the class generate a positive by default. I would also like to have a struct that represents the digits where there are two variants. The first variant has the digits 0-9 and the second which would inherit the original but also includes A-F. This way the class which would be a template class but only ever has two valid types would support use for Decimal and Hexadecimal. All of the mathematical operators would be defined outside of the class and depending its inferred type it will call and perform the appropriate version of the function. However the Hexadecimal part is still only concept as I'd like to get the Decimal version up and running first. These helper classes might look something like this:



class Sign {
private:
bool isNegative_ { false };
char sign_;
public:
Sign() : isNegative_( false ) {
sign_ = '+';
}
Sign( const bool isNegative ) : isNegative_( isNegative ) {
sign_ = isNegative_ ? '-' : '+';
};

Sign( const char sign ) {
if ( sign == '+' || sign == '' || sign == ' ' ) {
isNegative_ = false;
} else if ( sign == '-' ) {
isNegative_ = true;
} else {
// throw error improper character.
}
}

bool isPostive() const { return !isNegative_; }
bool isNegative() const { return !isNegative; }

char sign() const { return sign_; }
char negate() {
isNegative_ = !isNegative_;
sign_ = isNegative_ ? '+' : '-';
return sign_;
}
};

// NST = NumberSystemType
class enum NST { Decimal = 10, Hexadecimal = 16 };

template<class NT> // NT = NumberType
class Digit {
private:
NST nst_; // determines if Decimal or Hexadecimal
};

// Specialization for Decimal
template<NST::Decimal> // Should be same as template<10>
class Digit {
// would like some way to define 4 bits to represent 0-9; prefer not to
// use bitfields, but I think a char or unsigned char would be wasting
// memory using twice as much for what is needed. Not sure what to do here...
// maybe std::bitset<>...


};

template<NST::Hexadecimal> // Should be same as template<16>
class Digit : public Digit<Decimal> { // Also inherits all of The first specialization.
// same as above only needs 4 bits to represent values form 0-F
// A half char would be excellent but does not exist in c++... and most
// programming language's types.
// still thinking of maybe std::bitset<>...

};


The main distinction between the two is that the first specialization would only allow digits values from 0-9 and the digits themselves 0-9 where the second doesn't have that restriction, but also allows from a-f and or A-F either case is valid. I may also include a const char* to designate the Hex Prefix of 0x that would be appended to any contained value for display.



I like this design approach as I'd like to keep the actual arithmetic functions and operators of the BigInt class as seperate function templates since the BigInt class can support both Decimal and Hexadecimal specialized template types. Also down the road if everything goes properly I'd also like to add the support to work with Complex numbers as well.



The BigInt class would be like this:



template<class NT>
BigInt {
private:
Sign sign_;
Digit<NT> carryDigit_;
std::vector<Digit<NT>> value_;

// May contain some functions such as setters and getters only
// Do not want the class to be modifying itself except for assignment only.
};


And as above this would be specialized as well for Decimal and Hexadecimal Types, however if someone creates an instance of BigInt<> myBigInt this should default to Decimal!



For the data that is contained in the vector. I'd like to store the digits in reverse order of what one reads. So if their is a number 345698 in BigInt's internal vector it would be stored as 896543. The reason for this is when we do arithmetic operations in math we work from the least significant to the most significant starting at the right on the left side of the decimal point which is irrelevant since this is a BigInt only class and we work our way to the left. However if we store each digit that can only be 0-9 in each element of the above class's vector in the proper order and we use an outside operator+() function this would be challenging to do for one BigInt to another... example:



Basic Arithmetic R - L    | Vector storing standard
12345 <1,2,3,4,5>
+ 678 <6,7,8>
------
13023


Here the indexes of <5> & <8> do not coincide so this makes it tough to figure out how to add a value with a few digits to one with many. My approach is that if we store the number in reverse order:



                         | Vector stored in reverse
<5,4,3,2,1>
<6,7,8>


Then the addition becomes simple! All we have to do is add each digit from both BigInt's vectors by same index value. And we have the use of the carry digit to carry over to the next field. The resulting BigInt would return with a size that is at least equal to or greater than the largest of the two BigInts. If the carryDigit has a value then the operation of addition on the next iteration will include 3 additions instead of two. Now when getting the BigInt for display we can return either a vector> except that when the user gets it this is not a "BigInt" it is vector of Digits and it is also in the correct order. It can even be returned by a string representation. This class can be constructed by a vector> and it will reverse the order when it stores it internally.



This is the overall concept of my BigInt class, I'm just wondering if this is a good design plan, if it would be considered efficient and I guess the main question that I have is about what to use to store the actual digits within the Digit's class... Would std::bitset<> be appropriate to conserve the memory foot print or would it just be better to use a char and not worry about the extra space with the pro of being simpler to implement?










share|improve this question




















  • 1




    Is this as an excersice or for production? Because there's a whole bunch of tried and true bigint libraries out there with C++ interfaces, e.g. GMP.
    – Darhuuk
    Nov 8 at 9:03








  • 2




    If I was going to implement my own large-integer, I would use a built-in type such as 32-bits to hold each "digit"... essentially meaning my digit representation is base-2^32, rather than your proposal of base-10 or base-16. All my operations would be in binary and there would be no wasted space. So the entire number would just be something like std::vector<uint32_t>. This makes converting to/from base-10 for Input/Output slightly more involved, but the math would be fast and vectorizable... which to me is the whole point of big-int.
    – paddy
    Nov 8 at 9:17






  • 1




    "A half char would be excellent but does not exist in c++..." It doesn't exist in assembler either, because it makes no sense for processors designed to deal with 32 or 64 bits at a time with some internal convenience to deal with 8 bits at a time. And std::bitset won't help you with that since it allocates storage in word-size blocks (at least in the GCC implementation).
    – Nelfeal
    Nov 8 at 9:18








  • 2




    Well, it looks to me like you've looked at the problem, chosen an inappropriate solution and then over-engineered it.
    – paddy
    Nov 8 at 9:24






  • 1




    @FrancisCugler - I agree with paddy. Distinct types for hex and decimal is terribly flawed design. One requirement of integral types is being able to assign and compare them (e.g. for equality) to each other. Your approach means the number of operations you need to implement is quadratic with number of bases you support - hard to maintain. Consider using a single class to efficiently represent an unsigned bigint value, then implement a signed bigint using that unsigned bigint type and a bool to represent sign. Handle bases at input or output, not with distinct representation in memory.
    – Peter
    Nov 8 at 10:33















up vote
-1
down vote

favorite












I'm working on a concept at the moment and I'm writing the Pseudo code as I am asking this question. I'm thinking of making a fairly easy and simple to use class interface to represent BigInts. I'm thinking of making a couple of simple structures with basic properties and members that the BigInt class would use. For example instead of the BigInt class handling negative values directly it would contain a Sign struct and this struct would basically contain either a value of 0 or 1, or basically a Boolean type to designate if this BigInt is positive or negative. Upon Construction I intend to have the class generate a positive by default. I would also like to have a struct that represents the digits where there are two variants. The first variant has the digits 0-9 and the second which would inherit the original but also includes A-F. This way the class which would be a template class but only ever has two valid types would support use for Decimal and Hexadecimal. All of the mathematical operators would be defined outside of the class and depending its inferred type it will call and perform the appropriate version of the function. However the Hexadecimal part is still only concept as I'd like to get the Decimal version up and running first. These helper classes might look something like this:



class Sign {
private:
bool isNegative_ { false };
char sign_;
public:
Sign() : isNegative_( false ) {
sign_ = '+';
}
Sign( const bool isNegative ) : isNegative_( isNegative ) {
sign_ = isNegative_ ? '-' : '+';
};

Sign( const char sign ) {
if ( sign == '+' || sign == '' || sign == ' ' ) {
isNegative_ = false;
} else if ( sign == '-' ) {
isNegative_ = true;
} else {
// throw error improper character.
}
}

bool isPostive() const { return !isNegative_; }
bool isNegative() const { return !isNegative; }

char sign() const { return sign_; }
char negate() {
isNegative_ = !isNegative_;
sign_ = isNegative_ ? '+' : '-';
return sign_;
}
};

// NST = NumberSystemType
class enum NST { Decimal = 10, Hexadecimal = 16 };

template<class NT> // NT = NumberType
class Digit {
private:
NST nst_; // determines if Decimal or Hexadecimal
};

// Specialization for Decimal
template<NST::Decimal> // Should be same as template<10>
class Digit {
// would like some way to define 4 bits to represent 0-9; prefer not to
// use bitfields, but I think a char or unsigned char would be wasting
// memory using twice as much for what is needed. Not sure what to do here...
// maybe std::bitset<>...


};

template<NST::Hexadecimal> // Should be same as template<16>
class Digit : public Digit<Decimal> { // Also inherits all of The first specialization.
// same as above only needs 4 bits to represent values form 0-F
// A half char would be excellent but does not exist in c++... and most
// programming language's types.
// still thinking of maybe std::bitset<>...

};


The main distinction between the two is that the first specialization would only allow digits values from 0-9 and the digits themselves 0-9 where the second doesn't have that restriction, but also allows from a-f and or A-F either case is valid. I may also include a const char* to designate the Hex Prefix of 0x that would be appended to any contained value for display.



I like this design approach as I'd like to keep the actual arithmetic functions and operators of the BigInt class as seperate function templates since the BigInt class can support both Decimal and Hexadecimal specialized template types. Also down the road if everything goes properly I'd also like to add the support to work with Complex numbers as well.



The BigInt class would be like this:



template<class NT>
BigInt {
private:
Sign sign_;
Digit<NT> carryDigit_;
std::vector<Digit<NT>> value_;

// May contain some functions such as setters and getters only
// Do not want the class to be modifying itself except for assignment only.
};


And as above this would be specialized as well for Decimal and Hexadecimal Types, however if someone creates an instance of BigInt<> myBigInt this should default to Decimal!



For the data that is contained in the vector. I'd like to store the digits in reverse order of what one reads. So if their is a number 345698 in BigInt's internal vector it would be stored as 896543. The reason for this is when we do arithmetic operations in math we work from the least significant to the most significant starting at the right on the left side of the decimal point which is irrelevant since this is a BigInt only class and we work our way to the left. However if we store each digit that can only be 0-9 in each element of the above class's vector in the proper order and we use an outside operator+() function this would be challenging to do for one BigInt to another... example:



Basic Arithmetic R - L    | Vector storing standard
12345 <1,2,3,4,5>
+ 678 <6,7,8>
------
13023


Here the indexes of <5> & <8> do not coincide so this makes it tough to figure out how to add a value with a few digits to one with many. My approach is that if we store the number in reverse order:



                         | Vector stored in reverse
<5,4,3,2,1>
<6,7,8>


Then the addition becomes simple! All we have to do is add each digit from both BigInt's vectors by same index value. And we have the use of the carry digit to carry over to the next field. The resulting BigInt would return with a size that is at least equal to or greater than the largest of the two BigInts. If the carryDigit has a value then the operation of addition on the next iteration will include 3 additions instead of two. Now when getting the BigInt for display we can return either a vector> except that when the user gets it this is not a "BigInt" it is vector of Digits and it is also in the correct order. It can even be returned by a string representation. This class can be constructed by a vector> and it will reverse the order when it stores it internally.



This is the overall concept of my BigInt class, I'm just wondering if this is a good design plan, if it would be considered efficient and I guess the main question that I have is about what to use to store the actual digits within the Digit's class... Would std::bitset<> be appropriate to conserve the memory foot print or would it just be better to use a char and not worry about the extra space with the pro of being simpler to implement?










share|improve this question




















  • 1




    Is this as an excersice or for production? Because there's a whole bunch of tried and true bigint libraries out there with C++ interfaces, e.g. GMP.
    – Darhuuk
    Nov 8 at 9:03








  • 2




    If I was going to implement my own large-integer, I would use a built-in type such as 32-bits to hold each "digit"... essentially meaning my digit representation is base-2^32, rather than your proposal of base-10 or base-16. All my operations would be in binary and there would be no wasted space. So the entire number would just be something like std::vector<uint32_t>. This makes converting to/from base-10 for Input/Output slightly more involved, but the math would be fast and vectorizable... which to me is the whole point of big-int.
    – paddy
    Nov 8 at 9:17






  • 1




    "A half char would be excellent but does not exist in c++..." It doesn't exist in assembler either, because it makes no sense for processors designed to deal with 32 or 64 bits at a time with some internal convenience to deal with 8 bits at a time. And std::bitset won't help you with that since it allocates storage in word-size blocks (at least in the GCC implementation).
    – Nelfeal
    Nov 8 at 9:18








  • 2




    Well, it looks to me like you've looked at the problem, chosen an inappropriate solution and then over-engineered it.
    – paddy
    Nov 8 at 9:24






  • 1




    @FrancisCugler - I agree with paddy. Distinct types for hex and decimal is terribly flawed design. One requirement of integral types is being able to assign and compare them (e.g. for equality) to each other. Your approach means the number of operations you need to implement is quadratic with number of bases you support - hard to maintain. Consider using a single class to efficiently represent an unsigned bigint value, then implement a signed bigint using that unsigned bigint type and a bool to represent sign. Handle bases at input or output, not with distinct representation in memory.
    – Peter
    Nov 8 at 10:33













up vote
-1
down vote

favorite









up vote
-1
down vote

favorite











I'm working on a concept at the moment and I'm writing the Pseudo code as I am asking this question. I'm thinking of making a fairly easy and simple to use class interface to represent BigInts. I'm thinking of making a couple of simple structures with basic properties and members that the BigInt class would use. For example instead of the BigInt class handling negative values directly it would contain a Sign struct and this struct would basically contain either a value of 0 or 1, or basically a Boolean type to designate if this BigInt is positive or negative. Upon Construction I intend to have the class generate a positive by default. I would also like to have a struct that represents the digits where there are two variants. The first variant has the digits 0-9 and the second which would inherit the original but also includes A-F. This way the class which would be a template class but only ever has two valid types would support use for Decimal and Hexadecimal. All of the mathematical operators would be defined outside of the class and depending its inferred type it will call and perform the appropriate version of the function. However the Hexadecimal part is still only concept as I'd like to get the Decimal version up and running first. These helper classes might look something like this:



class Sign {
private:
bool isNegative_ { false };
char sign_;
public:
Sign() : isNegative_( false ) {
sign_ = '+';
}
Sign( const bool isNegative ) : isNegative_( isNegative ) {
sign_ = isNegative_ ? '-' : '+';
};

Sign( const char sign ) {
if ( sign == '+' || sign == '' || sign == ' ' ) {
isNegative_ = false;
} else if ( sign == '-' ) {
isNegative_ = true;
} else {
// throw error improper character.
}
}

bool isPostive() const { return !isNegative_; }
bool isNegative() const { return !isNegative; }

char sign() const { return sign_; }
char negate() {
isNegative_ = !isNegative_;
sign_ = isNegative_ ? '+' : '-';
return sign_;
}
};

// NST = NumberSystemType
class enum NST { Decimal = 10, Hexadecimal = 16 };

template<class NT> // NT = NumberType
class Digit {
private:
NST nst_; // determines if Decimal or Hexadecimal
};

// Specialization for Decimal
template<NST::Decimal> // Should be same as template<10>
class Digit {
// would like some way to define 4 bits to represent 0-9; prefer not to
// use bitfields, but I think a char or unsigned char would be wasting
// memory using twice as much for what is needed. Not sure what to do here...
// maybe std::bitset<>...


};

template<NST::Hexadecimal> // Should be same as template<16>
class Digit : public Digit<Decimal> { // Also inherits all of The first specialization.
// same as above only needs 4 bits to represent values form 0-F
// A half char would be excellent but does not exist in c++... and most
// programming language's types.
// still thinking of maybe std::bitset<>...

};


The main distinction between the two is that the first specialization would only allow digits values from 0-9 and the digits themselves 0-9 where the second doesn't have that restriction, but also allows from a-f and or A-F either case is valid. I may also include a const char* to designate the Hex Prefix of 0x that would be appended to any contained value for display.



I like this design approach as I'd like to keep the actual arithmetic functions and operators of the BigInt class as seperate function templates since the BigInt class can support both Decimal and Hexadecimal specialized template types. Also down the road if everything goes properly I'd also like to add the support to work with Complex numbers as well.



The BigInt class would be like this:



template<class NT>
BigInt {
private:
Sign sign_;
Digit<NT> carryDigit_;
std::vector<Digit<NT>> value_;

// May contain some functions such as setters and getters only
// Do not want the class to be modifying itself except for assignment only.
};


And as above this would be specialized as well for Decimal and Hexadecimal Types, however if someone creates an instance of BigInt<> myBigInt this should default to Decimal!



For the data that is contained in the vector. I'd like to store the digits in reverse order of what one reads. So if their is a number 345698 in BigInt's internal vector it would be stored as 896543. The reason for this is when we do arithmetic operations in math we work from the least significant to the most significant starting at the right on the left side of the decimal point which is irrelevant since this is a BigInt only class and we work our way to the left. However if we store each digit that can only be 0-9 in each element of the above class's vector in the proper order and we use an outside operator+() function this would be challenging to do for one BigInt to another... example:



Basic Arithmetic R - L    | Vector storing standard
12345 <1,2,3,4,5>
+ 678 <6,7,8>
------
13023


Here the indexes of <5> & <8> do not coincide so this makes it tough to figure out how to add a value with a few digits to one with many. My approach is that if we store the number in reverse order:



                         | Vector stored in reverse
<5,4,3,2,1>
<6,7,8>


Then the addition becomes simple! All we have to do is add each digit from both BigInt's vectors by same index value. And we have the use of the carry digit to carry over to the next field. The resulting BigInt would return with a size that is at least equal to or greater than the largest of the two BigInts. If the carryDigit has a value then the operation of addition on the next iteration will include 3 additions instead of two. Now when getting the BigInt for display we can return either a vector> except that when the user gets it this is not a "BigInt" it is vector of Digits and it is also in the correct order. It can even be returned by a string representation. This class can be constructed by a vector> and it will reverse the order when it stores it internally.



This is the overall concept of my BigInt class, I'm just wondering if this is a good design plan, if it would be considered efficient and I guess the main question that I have is about what to use to store the actual digits within the Digit's class... Would std::bitset<> be appropriate to conserve the memory foot print or would it just be better to use a char and not worry about the extra space with the pro of being simpler to implement?










share|improve this question















I'm working on a concept at the moment and I'm writing the Pseudo code as I am asking this question. I'm thinking of making a fairly easy and simple to use class interface to represent BigInts. I'm thinking of making a couple of simple structures with basic properties and members that the BigInt class would use. For example instead of the BigInt class handling negative values directly it would contain a Sign struct and this struct would basically contain either a value of 0 or 1, or basically a Boolean type to designate if this BigInt is positive or negative. Upon Construction I intend to have the class generate a positive by default. I would also like to have a struct that represents the digits where there are two variants. The first variant has the digits 0-9 and the second which would inherit the original but also includes A-F. This way the class which would be a template class but only ever has two valid types would support use for Decimal and Hexadecimal. All of the mathematical operators would be defined outside of the class and depending its inferred type it will call and perform the appropriate version of the function. However the Hexadecimal part is still only concept as I'd like to get the Decimal version up and running first. These helper classes might look something like this:



class Sign {
private:
bool isNegative_ { false };
char sign_;
public:
Sign() : isNegative_( false ) {
sign_ = '+';
}
Sign( const bool isNegative ) : isNegative_( isNegative ) {
sign_ = isNegative_ ? '-' : '+';
};

Sign( const char sign ) {
if ( sign == '+' || sign == '' || sign == ' ' ) {
isNegative_ = false;
} else if ( sign == '-' ) {
isNegative_ = true;
} else {
// throw error improper character.
}
}

bool isPostive() const { return !isNegative_; }
bool isNegative() const { return !isNegative; }

char sign() const { return sign_; }
char negate() {
isNegative_ = !isNegative_;
sign_ = isNegative_ ? '+' : '-';
return sign_;
}
};

// NST = NumberSystemType
class enum NST { Decimal = 10, Hexadecimal = 16 };

template<class NT> // NT = NumberType
class Digit {
private:
NST nst_; // determines if Decimal or Hexadecimal
};

// Specialization for Decimal
template<NST::Decimal> // Should be same as template<10>
class Digit {
// would like some way to define 4 bits to represent 0-9; prefer not to
// use bitfields, but I think a char or unsigned char would be wasting
// memory using twice as much for what is needed. Not sure what to do here...
// maybe std::bitset<>...


};

template<NST::Hexadecimal> // Should be same as template<16>
class Digit : public Digit<Decimal> { // Also inherits all of The first specialization.
// same as above only needs 4 bits to represent values form 0-F
// A half char would be excellent but does not exist in c++... and most
// programming language's types.
// still thinking of maybe std::bitset<>...

};


The main distinction between the two is that the first specialization would only allow digits values from 0-9 and the digits themselves 0-9 where the second doesn't have that restriction, but also allows from a-f and or A-F either case is valid. I may also include a const char* to designate the Hex Prefix of 0x that would be appended to any contained value for display.



I like this design approach as I'd like to keep the actual arithmetic functions and operators of the BigInt class as seperate function templates since the BigInt class can support both Decimal and Hexadecimal specialized template types. Also down the road if everything goes properly I'd also like to add the support to work with Complex numbers as well.



The BigInt class would be like this:



template<class NT>
BigInt {
private:
Sign sign_;
Digit<NT> carryDigit_;
std::vector<Digit<NT>> value_;

// May contain some functions such as setters and getters only
// Do not want the class to be modifying itself except for assignment only.
};


And as above this would be specialized as well for Decimal and Hexadecimal Types, however if someone creates an instance of BigInt<> myBigInt this should default to Decimal!



For the data that is contained in the vector. I'd like to store the digits in reverse order of what one reads. So if their is a number 345698 in BigInt's internal vector it would be stored as 896543. The reason for this is when we do arithmetic operations in math we work from the least significant to the most significant starting at the right on the left side of the decimal point which is irrelevant since this is a BigInt only class and we work our way to the left. However if we store each digit that can only be 0-9 in each element of the above class's vector in the proper order and we use an outside operator+() function this would be challenging to do for one BigInt to another... example:



Basic Arithmetic R - L    | Vector storing standard
12345 <1,2,3,4,5>
+ 678 <6,7,8>
------
13023


Here the indexes of <5> & <8> do not coincide so this makes it tough to figure out how to add a value with a few digits to one with many. My approach is that if we store the number in reverse order:



                         | Vector stored in reverse
<5,4,3,2,1>
<6,7,8>


Then the addition becomes simple! All we have to do is add each digit from both BigInt's vectors by same index value. And we have the use of the carry digit to carry over to the next field. The resulting BigInt would return with a size that is at least equal to or greater than the largest of the two BigInts. If the carryDigit has a value then the operation of addition on the next iteration will include 3 additions instead of two. Now when getting the BigInt for display we can return either a vector> except that when the user gets it this is not a "BigInt" it is vector of Digits and it is also in the correct order. It can even be returned by a string representation. This class can be constructed by a vector> and it will reverse the order when it stores it internally.



This is the overall concept of my BigInt class, I'm just wondering if this is a good design plan, if it would be considered efficient and I guess the main question that I have is about what to use to store the actual digits within the Digit's class... Would std::bitset<> be appropriate to conserve the memory foot print or would it just be better to use a char and not worry about the extra space with the pro of being simpler to implement?







c++ types biginteger template-specialization memory-footprint






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 8 at 9:07

























asked Nov 8 at 8:58









Francis Cugler

4,00711227




4,00711227








  • 1




    Is this as an excersice or for production? Because there's a whole bunch of tried and true bigint libraries out there with C++ interfaces, e.g. GMP.
    – Darhuuk
    Nov 8 at 9:03








  • 2




    If I was going to implement my own large-integer, I would use a built-in type such as 32-bits to hold each "digit"... essentially meaning my digit representation is base-2^32, rather than your proposal of base-10 or base-16. All my operations would be in binary and there would be no wasted space. So the entire number would just be something like std::vector<uint32_t>. This makes converting to/from base-10 for Input/Output slightly more involved, but the math would be fast and vectorizable... which to me is the whole point of big-int.
    – paddy
    Nov 8 at 9:17






  • 1




    "A half char would be excellent but does not exist in c++..." It doesn't exist in assembler either, because it makes no sense for processors designed to deal with 32 or 64 bits at a time with some internal convenience to deal with 8 bits at a time. And std::bitset won't help you with that since it allocates storage in word-size blocks (at least in the GCC implementation).
    – Nelfeal
    Nov 8 at 9:18








  • 2




    Well, it looks to me like you've looked at the problem, chosen an inappropriate solution and then over-engineered it.
    – paddy
    Nov 8 at 9:24






  • 1




    @FrancisCugler - I agree with paddy. Distinct types for hex and decimal is terribly flawed design. One requirement of integral types is being able to assign and compare them (e.g. for equality) to each other. Your approach means the number of operations you need to implement is quadratic with number of bases you support - hard to maintain. Consider using a single class to efficiently represent an unsigned bigint value, then implement a signed bigint using that unsigned bigint type and a bool to represent sign. Handle bases at input or output, not with distinct representation in memory.
    – Peter
    Nov 8 at 10:33














  • 1




    Is this as an excersice or for production? Because there's a whole bunch of tried and true bigint libraries out there with C++ interfaces, e.g. GMP.
    – Darhuuk
    Nov 8 at 9:03








  • 2




    If I was going to implement my own large-integer, I would use a built-in type such as 32-bits to hold each "digit"... essentially meaning my digit representation is base-2^32, rather than your proposal of base-10 or base-16. All my operations would be in binary and there would be no wasted space. So the entire number would just be something like std::vector<uint32_t>. This makes converting to/from base-10 for Input/Output slightly more involved, but the math would be fast and vectorizable... which to me is the whole point of big-int.
    – paddy
    Nov 8 at 9:17






  • 1




    "A half char would be excellent but does not exist in c++..." It doesn't exist in assembler either, because it makes no sense for processors designed to deal with 32 or 64 bits at a time with some internal convenience to deal with 8 bits at a time. And std::bitset won't help you with that since it allocates storage in word-size blocks (at least in the GCC implementation).
    – Nelfeal
    Nov 8 at 9:18








  • 2




    Well, it looks to me like you've looked at the problem, chosen an inappropriate solution and then over-engineered it.
    – paddy
    Nov 8 at 9:24






  • 1




    @FrancisCugler - I agree with paddy. Distinct types for hex and decimal is terribly flawed design. One requirement of integral types is being able to assign and compare them (e.g. for equality) to each other. Your approach means the number of operations you need to implement is quadratic with number of bases you support - hard to maintain. Consider using a single class to efficiently represent an unsigned bigint value, then implement a signed bigint using that unsigned bigint type and a bool to represent sign. Handle bases at input or output, not with distinct representation in memory.
    – Peter
    Nov 8 at 10:33








1




1




Is this as an excersice or for production? Because there's a whole bunch of tried and true bigint libraries out there with C++ interfaces, e.g. GMP.
– Darhuuk
Nov 8 at 9:03






Is this as an excersice or for production? Because there's a whole bunch of tried and true bigint libraries out there with C++ interfaces, e.g. GMP.
– Darhuuk
Nov 8 at 9:03






2




2




If I was going to implement my own large-integer, I would use a built-in type such as 32-bits to hold each "digit"... essentially meaning my digit representation is base-2^32, rather than your proposal of base-10 or base-16. All my operations would be in binary and there would be no wasted space. So the entire number would just be something like std::vector<uint32_t>. This makes converting to/from base-10 for Input/Output slightly more involved, but the math would be fast and vectorizable... which to me is the whole point of big-int.
– paddy
Nov 8 at 9:17




If I was going to implement my own large-integer, I would use a built-in type such as 32-bits to hold each "digit"... essentially meaning my digit representation is base-2^32, rather than your proposal of base-10 or base-16. All my operations would be in binary and there would be no wasted space. So the entire number would just be something like std::vector<uint32_t>. This makes converting to/from base-10 for Input/Output slightly more involved, but the math would be fast and vectorizable... which to me is the whole point of big-int.
– paddy
Nov 8 at 9:17




1




1




"A half char would be excellent but does not exist in c++..." It doesn't exist in assembler either, because it makes no sense for processors designed to deal with 32 or 64 bits at a time with some internal convenience to deal with 8 bits at a time. And std::bitset won't help you with that since it allocates storage in word-size blocks (at least in the GCC implementation).
– Nelfeal
Nov 8 at 9:18






"A half char would be excellent but does not exist in c++..." It doesn't exist in assembler either, because it makes no sense for processors designed to deal with 32 or 64 bits at a time with some internal convenience to deal with 8 bits at a time. And std::bitset won't help you with that since it allocates storage in word-size blocks (at least in the GCC implementation).
– Nelfeal
Nov 8 at 9:18






2




2




Well, it looks to me like you've looked at the problem, chosen an inappropriate solution and then over-engineered it.
– paddy
Nov 8 at 9:24




Well, it looks to me like you've looked at the problem, chosen an inappropriate solution and then over-engineered it.
– paddy
Nov 8 at 9:24




1




1




@FrancisCugler - I agree with paddy. Distinct types for hex and decimal is terribly flawed design. One requirement of integral types is being able to assign and compare them (e.g. for equality) to each other. Your approach means the number of operations you need to implement is quadratic with number of bases you support - hard to maintain. Consider using a single class to efficiently represent an unsigned bigint value, then implement a signed bigint using that unsigned bigint type and a bool to represent sign. Handle bases at input or output, not with distinct representation in memory.
– Peter
Nov 8 at 10:33




@FrancisCugler - I agree with paddy. Distinct types for hex and decimal is terribly flawed design. One requirement of integral types is being able to assign and compare them (e.g. for equality) to each other. Your approach means the number of operations you need to implement is quadratic with number of bases you support - hard to maintain. Consider using a single class to efficiently represent an unsigned bigint value, then implement a signed bigint using that unsigned bigint type and a bool to represent sign. Handle bases at input or output, not with distinct representation in memory.
– Peter
Nov 8 at 10:33












2 Answers
2






active

oldest

votes

















up vote
0
down vote













The smallest addressable unit of memory in C++ is a byte, and a byte is at least 8 bits (and usually exactly 8 bits). So your idea indeed wastes memory.



I also suspect you're not that familiar with math. What you call "NumberSystemType" is ordinarily called the base. So we talk about base-10 or base-16.



Now base-10 is only useful for human consumption, and thus pointless for this sort of bigints. Humans can't deal with numbers that have more than about 20 decimal digits anyway. So, pick a base that's useful for computers. And pick one. There's no need to support multiple bases. As paddy points out, base 2^32 is a perfectly reasonable base for computers.






share|improve this answer





















  • I understand the distinction of what you are saying about base-10 and base-16 due to Logarithms. I was just giving it a friendly readable name As base-10 which is Decimal is a Number System and base-16 which is Decimal is also a Number System. I'm trying to only store in the Decimal version of BigInt's container for each element 0-9 and nothing else but as for the Hexadecimal version it will store 0-F. The reason for this is when writing the functions to do the addition - subtraction etc. they become quite easy if the digits are stored in the vector in reverse order!
    – Francis Cugler
    Nov 8 at 9:27






  • 2




    @FrancisCugler: Oh, the idea of storing digits in reverse order is not bad. But that still works when you choose a reasonable base like 2^8 or 2^32. Choosing a too-small base like 2^4 is just adding problems without any benefits whatsoever.
    – MSalters
    Nov 8 at 9:32






  • 2




    @FrancisCugler: Yeah, I already suspected you missed the necessary math basis. < 9, 2334343, 235, 3, 243 > are 5 perfectly decent digits in base 2^32. Of course, there's no digit "03" there. If anything, it's 0x00000003.
    – MSalters
    Nov 8 at 9:52






  • 1




    To answer the above: yes, and maybe (not if you multiply as 64-bit for example, or use special instructions that store the overflow, useful for exactly these scenarios). This is one reason why I suggested you work in base-256, until you start to understand how to manage things like overflow in 32-bit integers. There's a clear knowledge gap here that's difficult to address as any kind of answer to your question. Your main issue was needing to be space-efficient. We've repeatedly told you how to achieve that, and all that remains is for you to go try it, and learn along the way.
    – paddy
    Nov 8 at 10:29






  • 1




    Yes, that's exactly what we're saying.
    – paddy
    Nov 8 at 10:45


















up vote
0
down vote













-Update-



Okay I took some advise and this is what I have so far. I have not yet incorporated the concept of storing the values in reverse order which shouldn't be all too difficult. As it currently stands, I have eliminated all of the outside classes and my BigInt class signature looks like this:



class BigInt {
private:
short sign_;
uint32_t carry_{0};
std::vector<uint32_t> value_;

public:
BigInt() : sign_( 0 ) {}

BigInt( const uint32_t* value, const std::size_t arraySize, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), &value[0], &value[arraySize] );
std::reverse( value_.begin(), value_.end() );
}

BigInt( const std::vector<uint32_t>& value, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), value.begin(), value.end() );
std::reverse( value_.begin(), value_.end() );
}

BigInt( std::initializer_list<uint32_t> values, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), values.begin(), values.end() );
std::reverse( value_.begin(), value_.end() );
}

std::vector<uint32_t> operator()() {
//value_.push_back( static_cast<uint32_t>( sign_ ) ); // causes negative to be pushed into container multiple times...
std::reverse( value_.begin(), value_.end() );
return value_;
}
};


I think this is a good start: there are several ways to construct one which gives the caller some flexibility since you can create a BigInt with a pointer, an array, a vector, or even an initializer list. At first it may seem counter intuitive to have the sign at the end of the constructor but I want to be able to have the sign value to be a defaulted parameter when the value is positive. If anything other than 0 is passed in for the sign value it will convert it to -1. A little more time and I'll have the signature of this class complete then I can move onto the operators that will work on it.



There are some minor pitfalls in the above code but I'm continuously working on them.






share|improve this answer























  • The smallest addressable storage unit is char. If you insist on templating your 'digit' type as a base-10 integral type, then you'll either need to accept wasting a lot of bits, or pack your numbers as BCD (i.e. two digits per byte) and index them in pairs. If your math is lightweight and the main processing requirement is converting to/from base 10 then carry on. If the main processing requirement is fast math and compact storage, then you are on the wrong track and you should use a base that is a power of 2 that can be packed into a native integral type.
    – paddy
    Nov 8 at 10:33












  • @paddy I updated my answer with working code: it's not complete and bug free yet, but it is a work in progress. This class will basically only store the contents, manage its element ordering and checking for valid input. All of the operations on this I am planning on implementing in outside functions that will be friends with this class.
    – Francis Cugler
    Nov 9 at 16:05











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53204355%2freduce-memory-footprint-in-the-construction-of-a-bigint-class%23new-answer', 'question_page');
}
);

Post as a guest
































2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
0
down vote













The smallest addressable unit of memory in C++ is a byte, and a byte is at least 8 bits (and usually exactly 8 bits). So your idea indeed wastes memory.



I also suspect you're not that familiar with math. What you call "NumberSystemType" is ordinarily called the base. So we talk about base-10 or base-16.



Now base-10 is only useful for human consumption, and thus pointless for this sort of bigints. Humans can't deal with numbers that have more than about 20 decimal digits anyway. So, pick a base that's useful for computers. And pick one. There's no need to support multiple bases. As paddy points out, base 2^32 is a perfectly reasonable base for computers.






share|improve this answer





















  • I understand the distinction of what you are saying about base-10 and base-16 due to Logarithms. I was just giving it a friendly readable name As base-10 which is Decimal is a Number System and base-16 which is Decimal is also a Number System. I'm trying to only store in the Decimal version of BigInt's container for each element 0-9 and nothing else but as for the Hexadecimal version it will store 0-F. The reason for this is when writing the functions to do the addition - subtraction etc. they become quite easy if the digits are stored in the vector in reverse order!
    – Francis Cugler
    Nov 8 at 9:27






  • 2




    @FrancisCugler: Oh, the idea of storing digits in reverse order is not bad. But that still works when you choose a reasonable base like 2^8 or 2^32. Choosing a too-small base like 2^4 is just adding problems without any benefits whatsoever.
    – MSalters
    Nov 8 at 9:32






  • 2




    @FrancisCugler: Yeah, I already suspected you missed the necessary math basis. < 9, 2334343, 235, 3, 243 > are 5 perfectly decent digits in base 2^32. Of course, there's no digit "03" there. If anything, it's 0x00000003.
    – MSalters
    Nov 8 at 9:52






  • 1




    To answer the above: yes, and maybe (not if you multiply as 64-bit for example, or use special instructions that store the overflow, useful for exactly these scenarios). This is one reason why I suggested you work in base-256, until you start to understand how to manage things like overflow in 32-bit integers. There's a clear knowledge gap here that's difficult to address as any kind of answer to your question. Your main issue was needing to be space-efficient. We've repeatedly told you how to achieve that, and all that remains is for you to go try it, and learn along the way.
    – paddy
    Nov 8 at 10:29






  • 1




    Yes, that's exactly what we're saying.
    – paddy
    Nov 8 at 10:45















up vote
0
down vote













The smallest addressable unit of memory in C++ is a byte, and a byte is at least 8 bits (and usually exactly 8 bits). So your idea indeed wastes memory.



I also suspect you're not that familiar with math. What you call "NumberSystemType" is ordinarily called the base. So we talk about base-10 or base-16.



Now base-10 is only useful for human consumption, and thus pointless for this sort of bigints. Humans can't deal with numbers that have more than about 20 decimal digits anyway. So, pick a base that's useful for computers. And pick one. There's no need to support multiple bases. As paddy points out, base 2^32 is a perfectly reasonable base for computers.






share|improve this answer





















  • I understand the distinction of what you are saying about base-10 and base-16 due to Logarithms. I was just giving it a friendly readable name As base-10 which is Decimal is a Number System and base-16 which is Decimal is also a Number System. I'm trying to only store in the Decimal version of BigInt's container for each element 0-9 and nothing else but as for the Hexadecimal version it will store 0-F. The reason for this is when writing the functions to do the addition - subtraction etc. they become quite easy if the digits are stored in the vector in reverse order!
    – Francis Cugler
    Nov 8 at 9:27






  • 2




    @FrancisCugler: Oh, the idea of storing digits in reverse order is not bad. But that still works when you choose a reasonable base like 2^8 or 2^32. Choosing a too-small base like 2^4 is just adding problems without any benefits whatsoever.
    – MSalters
    Nov 8 at 9:32






  • 2




    @FrancisCugler: Yeah, I already suspected you missed the necessary math basis. < 9, 2334343, 235, 3, 243 > are 5 perfectly decent digits in base 2^32. Of course, there's no digit "03" there. If anything, it's 0x00000003.
    – MSalters
    Nov 8 at 9:52






  • 1




    To answer the above: yes, and maybe (not if you multiply as 64-bit for example, or use special instructions that store the overflow, useful for exactly these scenarios). This is one reason why I suggested you work in base-256, until you start to understand how to manage things like overflow in 32-bit integers. There's a clear knowledge gap here that's difficult to address as any kind of answer to your question. Your main issue was needing to be space-efficient. We've repeatedly told you how to achieve that, and all that remains is for you to go try it, and learn along the way.
    – paddy
    Nov 8 at 10:29






  • 1




    Yes, that's exactly what we're saying.
    – paddy
    Nov 8 at 10:45













up vote
0
down vote










up vote
0
down vote









The smallest addressable unit of memory in C++ is a byte, and a byte is at least 8 bits (and usually exactly 8 bits). So your idea indeed wastes memory.



I also suspect you're not that familiar with math. What you call "NumberSystemType" is ordinarily called the base. So we talk about base-10 or base-16.



Now base-10 is only useful for human consumption, and thus pointless for this sort of bigints. Humans can't deal with numbers that have more than about 20 decimal digits anyway. So, pick a base that's useful for computers. And pick one. There's no need to support multiple bases. As paddy points out, base 2^32 is a perfectly reasonable base for computers.






share|improve this answer












The smallest addressable unit of memory in C++ is a byte, and a byte is at least 8 bits (and usually exactly 8 bits). So your idea indeed wastes memory.



I also suspect you're not that familiar with math. What you call "NumberSystemType" is ordinarily called the base. So we talk about base-10 or base-16.



Now base-10 is only useful for human consumption, and thus pointless for this sort of bigints. Humans can't deal with numbers that have more than about 20 decimal digits anyway. So, pick a base that's useful for computers. And pick one. There's no need to support multiple bases. As paddy points out, base 2^32 is a perfectly reasonable base for computers.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 8 at 9:22









MSalters

132k8115267




132k8115267












  • I understand the distinction of what you are saying about base-10 and base-16 due to Logarithms. I was just giving it a friendly readable name As base-10 which is Decimal is a Number System and base-16 which is Decimal is also a Number System. I'm trying to only store in the Decimal version of BigInt's container for each element 0-9 and nothing else but as for the Hexadecimal version it will store 0-F. The reason for this is when writing the functions to do the addition - subtraction etc. they become quite easy if the digits are stored in the vector in reverse order!
    – Francis Cugler
    Nov 8 at 9:27






  • 2




    @FrancisCugler: Oh, the idea of storing digits in reverse order is not bad. But that still works when you choose a reasonable base like 2^8 or 2^32. Choosing a too-small base like 2^4 is just adding problems without any benefits whatsoever.
    – MSalters
    Nov 8 at 9:32






  • 2




    @FrancisCugler: Yeah, I already suspected you missed the necessary math basis. < 9, 2334343, 235, 3, 243 > are 5 perfectly decent digits in base 2^32. Of course, there's no digit "03" there. If anything, it's 0x00000003.
    – MSalters
    Nov 8 at 9:52






  • 1




    To answer the above: yes, and maybe (not if you multiply as 64-bit for example, or use special instructions that store the overflow, useful for exactly these scenarios). This is one reason why I suggested you work in base-256, until you start to understand how to manage things like overflow in 32-bit integers. There's a clear knowledge gap here that's difficult to address as any kind of answer to your question. Your main issue was needing to be space-efficient. We've repeatedly told you how to achieve that, and all that remains is for you to go try it, and learn along the way.
    – paddy
    Nov 8 at 10:29






  • 1




    Yes, that's exactly what we're saying.
    – paddy
    Nov 8 at 10:45


















  • I understand the distinction of what you are saying about base-10 and base-16 due to Logarithms. I was just giving it a friendly readable name As base-10 which is Decimal is a Number System and base-16 which is Decimal is also a Number System. I'm trying to only store in the Decimal version of BigInt's container for each element 0-9 and nothing else but as for the Hexadecimal version it will store 0-F. The reason for this is when writing the functions to do the addition - subtraction etc. they become quite easy if the digits are stored in the vector in reverse order!
    – Francis Cugler
    Nov 8 at 9:27






  • 2




    @FrancisCugler: Oh, the idea of storing digits in reverse order is not bad. But that still works when you choose a reasonable base like 2^8 or 2^32. Choosing a too-small base like 2^4 is just adding problems without any benefits whatsoever.
    – MSalters
    Nov 8 at 9:32






  • 2




    @FrancisCugler: Yeah, I already suspected you missed the necessary math basis. < 9, 2334343, 235, 3, 243 > are 5 perfectly decent digits in base 2^32. Of course, there's no digit "03" there. If anything, it's 0x00000003.
    – MSalters
    Nov 8 at 9:52






  • 1




    To answer the above: yes, and maybe (not if you multiply as 64-bit for example, or use special instructions that store the overflow, useful for exactly these scenarios). This is one reason why I suggested you work in base-256, until you start to understand how to manage things like overflow in 32-bit integers. There's a clear knowledge gap here that's difficult to address as any kind of answer to your question. Your main issue was needing to be space-efficient. We've repeatedly told you how to achieve that, and all that remains is for you to go try it, and learn along the way.
    – paddy
    Nov 8 at 10:29






  • 1




    Yes, that's exactly what we're saying.
    – paddy
    Nov 8 at 10:45
















I understand the distinction of what you are saying about base-10 and base-16 due to Logarithms. I was just giving it a friendly readable name As base-10 which is Decimal is a Number System and base-16 which is Decimal is also a Number System. I'm trying to only store in the Decimal version of BigInt's container for each element 0-9 and nothing else but as for the Hexadecimal version it will store 0-F. The reason for this is when writing the functions to do the addition - subtraction etc. they become quite easy if the digits are stored in the vector in reverse order!
– Francis Cugler
Nov 8 at 9:27




I understand the distinction of what you are saying about base-10 and base-16 due to Logarithms. I was just giving it a friendly readable name As base-10 which is Decimal is a Number System and base-16 which is Decimal is also a Number System. I'm trying to only store in the Decimal version of BigInt's container for each element 0-9 and nothing else but as for the Hexadecimal version it will store 0-F. The reason for this is when writing the functions to do the addition - subtraction etc. they become quite easy if the digits are stored in the vector in reverse order!
– Francis Cugler
Nov 8 at 9:27




2




2




@FrancisCugler: Oh, the idea of storing digits in reverse order is not bad. But that still works when you choose a reasonable base like 2^8 or 2^32. Choosing a too-small base like 2^4 is just adding problems without any benefits whatsoever.
– MSalters
Nov 8 at 9:32




@FrancisCugler: Oh, the idea of storing digits in reverse order is not bad. But that still works when you choose a reasonable base like 2^8 or 2^32. Choosing a too-small base like 2^4 is just adding problems without any benefits whatsoever.
– MSalters
Nov 8 at 9:32




2




2




@FrancisCugler: Yeah, I already suspected you missed the necessary math basis. < 9, 2334343, 235, 3, 243 > are 5 perfectly decent digits in base 2^32. Of course, there's no digit "03" there. If anything, it's 0x00000003.
– MSalters
Nov 8 at 9:52




@FrancisCugler: Yeah, I already suspected you missed the necessary math basis. < 9, 2334343, 235, 3, 243 > are 5 perfectly decent digits in base 2^32. Of course, there's no digit "03" there. If anything, it's 0x00000003.
– MSalters
Nov 8 at 9:52




1




1




To answer the above: yes, and maybe (not if you multiply as 64-bit for example, or use special instructions that store the overflow, useful for exactly these scenarios). This is one reason why I suggested you work in base-256, until you start to understand how to manage things like overflow in 32-bit integers. There's a clear knowledge gap here that's difficult to address as any kind of answer to your question. Your main issue was needing to be space-efficient. We've repeatedly told you how to achieve that, and all that remains is for you to go try it, and learn along the way.
– paddy
Nov 8 at 10:29




To answer the above: yes, and maybe (not if you multiply as 64-bit for example, or use special instructions that store the overflow, useful for exactly these scenarios). This is one reason why I suggested you work in base-256, until you start to understand how to manage things like overflow in 32-bit integers. There's a clear knowledge gap here that's difficult to address as any kind of answer to your question. Your main issue was needing to be space-efficient. We've repeatedly told you how to achieve that, and all that remains is for you to go try it, and learn along the way.
– paddy
Nov 8 at 10:29




1




1




Yes, that's exactly what we're saying.
– paddy
Nov 8 at 10:45




Yes, that's exactly what we're saying.
– paddy
Nov 8 at 10:45












up vote
0
down vote













-Update-



Okay I took some advise and this is what I have so far. I have not yet incorporated the concept of storing the values in reverse order which shouldn't be all too difficult. As it currently stands, I have eliminated all of the outside classes and my BigInt class signature looks like this:



class BigInt {
private:
short sign_;
uint32_t carry_{0};
std::vector<uint32_t> value_;

public:
BigInt() : sign_( 0 ) {}

BigInt( const uint32_t* value, const std::size_t arraySize, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), &value[0], &value[arraySize] );
std::reverse( value_.begin(), value_.end() );
}

BigInt( const std::vector<uint32_t>& value, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), value.begin(), value.end() );
std::reverse( value_.begin(), value_.end() );
}

BigInt( std::initializer_list<uint32_t> values, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), values.begin(), values.end() );
std::reverse( value_.begin(), value_.end() );
}

std::vector<uint32_t> operator()() {
//value_.push_back( static_cast<uint32_t>( sign_ ) ); // causes negative to be pushed into container multiple times...
std::reverse( value_.begin(), value_.end() );
return value_;
}
};


I think this is a good start: there are several ways to construct one which gives the caller some flexibility since you can create a BigInt with a pointer, an array, a vector, or even an initializer list. At first it may seem counter intuitive to have the sign at the end of the constructor but I want to be able to have the sign value to be a defaulted parameter when the value is positive. If anything other than 0 is passed in for the sign value it will convert it to -1. A little more time and I'll have the signature of this class complete then I can move onto the operators that will work on it.



There are some minor pitfalls in the above code but I'm continuously working on them.






share|improve this answer























  • The smallest addressable storage unit is char. If you insist on templating your 'digit' type as a base-10 integral type, then you'll either need to accept wasting a lot of bits, or pack your numbers as BCD (i.e. two digits per byte) and index them in pairs. If your math is lightweight and the main processing requirement is converting to/from base 10 then carry on. If the main processing requirement is fast math and compact storage, then you are on the wrong track and you should use a base that is a power of 2 that can be packed into a native integral type.
    – paddy
    Nov 8 at 10:33












  • @paddy I updated my answer with working code: it's not complete and bug free yet, but it is a work in progress. This class will basically only store the contents, manage its element ordering and checking for valid input. All of the operations on this I am planning on implementing in outside functions that will be friends with this class.
    – Francis Cugler
    Nov 9 at 16:05















up vote
0
down vote













-Update-



Okay I took some advise and this is what I have so far. I have not yet incorporated the concept of storing the values in reverse order which shouldn't be all too difficult. As it currently stands, I have eliminated all of the outside classes and my BigInt class signature looks like this:



class BigInt {
private:
short sign_;
uint32_t carry_{0};
std::vector<uint32_t> value_;

public:
BigInt() : sign_( 0 ) {}

BigInt( const uint32_t* value, const std::size_t arraySize, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), &value[0], &value[arraySize] );
std::reverse( value_.begin(), value_.end() );
}

BigInt( const std::vector<uint32_t>& value, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), value.begin(), value.end() );
std::reverse( value_.begin(), value_.end() );
}

BigInt( std::initializer_list<uint32_t> values, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), values.begin(), values.end() );
std::reverse( value_.begin(), value_.end() );
}

std::vector<uint32_t> operator()() {
//value_.push_back( static_cast<uint32_t>( sign_ ) ); // causes negative to be pushed into container multiple times...
std::reverse( value_.begin(), value_.end() );
return value_;
}
};


I think this is a good start: there are several ways to construct one which gives the caller some flexibility since you can create a BigInt with a pointer, an array, a vector, or even an initializer list. At first it may seem counter intuitive to have the sign at the end of the constructor but I want to be able to have the sign value to be a defaulted parameter when the value is positive. If anything other than 0 is passed in for the sign value it will convert it to -1. A little more time and I'll have the signature of this class complete then I can move onto the operators that will work on it.



There are some minor pitfalls in the above code but I'm continuously working on them.






share|improve this answer























  • The smallest addressable storage unit is char. If you insist on templating your 'digit' type as a base-10 integral type, then you'll either need to accept wasting a lot of bits, or pack your numbers as BCD (i.e. two digits per byte) and index them in pairs. If your math is lightweight and the main processing requirement is converting to/from base 10 then carry on. If the main processing requirement is fast math and compact storage, then you are on the wrong track and you should use a base that is a power of 2 that can be packed into a native integral type.
    – paddy
    Nov 8 at 10:33












  • @paddy I updated my answer with working code: it's not complete and bug free yet, but it is a work in progress. This class will basically only store the contents, manage its element ordering and checking for valid input. All of the operations on this I am planning on implementing in outside functions that will be friends with this class.
    – Francis Cugler
    Nov 9 at 16:05













up vote
0
down vote










up vote
0
down vote









-Update-



Okay I took some advise and this is what I have so far. I have not yet incorporated the concept of storing the values in reverse order which shouldn't be all too difficult. As it currently stands, I have eliminated all of the outside classes and my BigInt class signature looks like this:



class BigInt {
private:
short sign_;
uint32_t carry_{0};
std::vector<uint32_t> value_;

public:
BigInt() : sign_( 0 ) {}

BigInt( const uint32_t* value, const std::size_t arraySize, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), &value[0], &value[arraySize] );
std::reverse( value_.begin(), value_.end() );
}

BigInt( const std::vector<uint32_t>& value, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), value.begin(), value.end() );
std::reverse( value_.begin(), value_.end() );
}

BigInt( std::initializer_list<uint32_t> values, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), values.begin(), values.end() );
std::reverse( value_.begin(), value_.end() );
}

std::vector<uint32_t> operator()() {
//value_.push_back( static_cast<uint32_t>( sign_ ) ); // causes negative to be pushed into container multiple times...
std::reverse( value_.begin(), value_.end() );
return value_;
}
};


I think this is a good start: there are several ways to construct one which gives the caller some flexibility since you can create a BigInt with a pointer, an array, a vector, or even an initializer list. At first it may seem counter intuitive to have the sign at the end of the constructor but I want to be able to have the sign value to be a defaulted parameter when the value is positive. If anything other than 0 is passed in for the sign value it will convert it to -1. A little more time and I'll have the signature of this class complete then I can move onto the operators that will work on it.



There are some minor pitfalls in the above code but I'm continuously working on them.






share|improve this answer














-Update-



Okay I took some advise and this is what I have so far. I have not yet incorporated the concept of storing the values in reverse order which shouldn't be all too difficult. As it currently stands, I have eliminated all of the outside classes and my BigInt class signature looks like this:



class BigInt {
private:
short sign_;
uint32_t carry_{0};
std::vector<uint32_t> value_;

public:
BigInt() : sign_( 0 ) {}

BigInt( const uint32_t* value, const std::size_t arraySize, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), &value[0], &value[arraySize] );
std::reverse( value_.begin(), value_.end() );
}

BigInt( const std::vector<uint32_t>& value, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), value.begin(), value.end() );
std::reverse( value_.begin(), value_.end() );
}

BigInt( std::initializer_list<uint32_t> values, short sign = 0 ) :
sign_( sign ) {
if( sign_ != 0 ) sign_ = -1;
value_.insert( value_.end(), values.begin(), values.end() );
std::reverse( value_.begin(), value_.end() );
}

std::vector<uint32_t> operator()() {
//value_.push_back( static_cast<uint32_t>( sign_ ) ); // causes negative to be pushed into container multiple times...
std::reverse( value_.begin(), value_.end() );
return value_;
}
};


I think this is a good start: there are several ways to construct one which gives the caller some flexibility since you can create a BigInt with a pointer, an array, a vector, or even an initializer list. At first it may seem counter intuitive to have the sign at the end of the constructor but I want to be able to have the sign value to be a defaulted parameter when the value is positive. If anything other than 0 is passed in for the sign value it will convert it to -1. A little more time and I'll have the signature of this class complete then I can move onto the operators that will work on it.



There are some minor pitfalls in the above code but I'm continuously working on them.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 9 at 1:35

























answered Nov 8 at 10:13









Francis Cugler

4,00711227




4,00711227












  • The smallest addressable storage unit is char. If you insist on templating your 'digit' type as a base-10 integral type, then you'll either need to accept wasting a lot of bits, or pack your numbers as BCD (i.e. two digits per byte) and index them in pairs. If your math is lightweight and the main processing requirement is converting to/from base 10 then carry on. If the main processing requirement is fast math and compact storage, then you are on the wrong track and you should use a base that is a power of 2 that can be packed into a native integral type.
    – paddy
    Nov 8 at 10:33












  • @paddy I updated my answer with working code: it's not complete and bug free yet, but it is a work in progress. This class will basically only store the contents, manage its element ordering and checking for valid input. All of the operations on this I am planning on implementing in outside functions that will be friends with this class.
    – Francis Cugler
    Nov 9 at 16:05


















  • The smallest addressable storage unit is char. If you insist on templating your 'digit' type as a base-10 integral type, then you'll either need to accept wasting a lot of bits, or pack your numbers as BCD (i.e. two digits per byte) and index them in pairs. If your math is lightweight and the main processing requirement is converting to/from base 10 then carry on. If the main processing requirement is fast math and compact storage, then you are on the wrong track and you should use a base that is a power of 2 that can be packed into a native integral type.
    – paddy
    Nov 8 at 10:33












  • @paddy I updated my answer with working code: it's not complete and bug free yet, but it is a work in progress. This class will basically only store the contents, manage its element ordering and checking for valid input. All of the operations on this I am planning on implementing in outside functions that will be friends with this class.
    – Francis Cugler
    Nov 9 at 16:05
















The smallest addressable storage unit is char. If you insist on templating your 'digit' type as a base-10 integral type, then you'll either need to accept wasting a lot of bits, or pack your numbers as BCD (i.e. two digits per byte) and index them in pairs. If your math is lightweight and the main processing requirement is converting to/from base 10 then carry on. If the main processing requirement is fast math and compact storage, then you are on the wrong track and you should use a base that is a power of 2 that can be packed into a native integral type.
– paddy
Nov 8 at 10:33






The smallest addressable storage unit is char. If you insist on templating your 'digit' type as a base-10 integral type, then you'll either need to accept wasting a lot of bits, or pack your numbers as BCD (i.e. two digits per byte) and index them in pairs. If your math is lightweight and the main processing requirement is converting to/from base 10 then carry on. If the main processing requirement is fast math and compact storage, then you are on the wrong track and you should use a base that is a power of 2 that can be packed into a native integral type.
– paddy
Nov 8 at 10:33














@paddy I updated my answer with working code: it's not complete and bug free yet, but it is a work in progress. This class will basically only store the contents, manage its element ordering and checking for valid input. All of the operations on this I am planning on implementing in outside functions that will be friends with this class.
– Francis Cugler
Nov 9 at 16:05




@paddy I updated my answer with working code: it's not complete and bug free yet, but it is a work in progress. This class will basically only store the contents, manage its element ordering and checking for valid input. All of the operations on this I am planning on implementing in outside functions that will be friends with this class.
– Francis Cugler
Nov 9 at 16:05


















 

draft saved


draft discarded



















































 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53204355%2freduce-memory-footprint-in-the-construction-of-a-bigint-class%23new-answer', 'question_page');
}
);

Post as a guest




















































































Popular posts from this blog

Schultheiß

Verwaltungsgliederung Dänemarks

Liste der Kulturdenkmale in Wilsdruff