Click here to Skip to main content
Click here to Skip to main content

C++ Integer Class

By , 5 Jul 2004
 

The author no longer maintains this project.

Sample Image - CppIntegerClass.png

Introduction

This is a class which encapsulates very large integers, using a fixed array of double words. It is coded in C++ and uses some inline assembly.

Background

The biggest problem implementing class Integer was figuring out how to represent the data. The actual data would be stored in a member array: Unit Data[MaxSize], where Unit is a typedef of unsigned __int32, and MaxSize is a static const of value 32 (which can be changed). I decided that Data would not be a pointer to a heap array because the code runs three times faster without calling new or realloc for every function. UnitLen is an unsigned which keeps track of how many members of array Data are used.

The biggest problem was deciding if an integer is positive, negative, or zero, because it would be inefficient to scan Data from 0 to UnitLen-1 to discern between a positive number and zero. int SigBit keeps track of both the sign of the Integer and the most significant bit of data. If SigBit is equal to zero, then the Integer is zero. If SigBit is positive, then it equals the index of the most significant bit plus one. If SigBit is negative, then it equals the negation of the index of the most significant bit minus 2. For example, for 0x00000002, SigBit = 2, and for 0xFFFFFFFE, SigBit = -2.

I implemented some of the arithmetic functions using inline assembly in order to cut down the time. In other places, I used regular C++ arithmetic (instead of bit-manipulation, as in the non-assembly half of Integer::Div()) to speed up a few parts of the code. For example, the algorithms for Integer::Mul() or Integer::Div() are simpler and faster if one of the parameters is small enough to take up only one Unit of Data.

Using the Code

class Integer takes up quite a bit of stack space if it is allocated there because operations are much faster. Arithmetic functions are almost all static functions, although overloaded operators would be easy to implement. (I don't use overloaded operators for a big object like class Integer because I don't like hidden temporary variables). The addresses of parameters are checked to handle "in" parameters that are also "out" parameters. This is the declaration of class Integer:

// class Integer v.90
// Copyright Cap'n Code (C) 2004 
// "Use this without giving me credit and I'll sue!"
#pragma once
#include <iostream>

class Integer
{
public:
    typedef unsigned    __int32 Unit;
    static const unsigned MaxSize = 32;

protected:
    // Number of units that store data
    unsigned    UnitLen;

    // If > 0, (SigBit - 1) is the offset of the highest set bit
    // If < 0, (-SigBit - 2) is the offset of the highest set bit,
    // or is -1 for Integer(-1)
    // Otherwise, Integer(0)
    signed    SigBit;

    // Actual data
    Unit Data[MaxSize];

public:
    Integer(void);
    Integer(const Integer &);
    Integer(const Unit *, unsigned Count);
    Integer(const char *, unsigned Radix);
    Integer(const __wchar_t *, unsigned Radix);
    explicit Integer(signed long);

    void Not();    // 1's completement
    void Neg(); // 2's completement
    bool ShiftLeft();
    bool ShiftRight();

    long GetLong() const;
    void Copy(const Unit *, unsigned Count);
    void FromString(const char *, unsigned Radix);
    void FromString(const __wchar_t *, unsigned Radix);
    void MoveTo(Integer &);    // Moves data and clears this
    void ToString(char *, unsigned Radix) const;
    void ToString(__wchar_t *, unsigned Radix) const;

    unsigned GetUnitLength() const;
    unsigned GetSigUnitLen() const;
    Unit GetUnit(unsigned) const;
    bool GetBit(unsigned) const;

    bool IsZero() const;
    bool IsNegative() const;
    bool IsPositive() const;
    bool IsEven() const;

    Integer & operator = (signed long);
    Integer & operator = (const Integer &);

    bool operator == (const Integer &) const;
    bool operator != (const Integer &) const;
    bool operator <  (const Integer &) const;
    bool operator >  (const Integer &) const;
    bool operator <= (const Integer &) const;
    bool operator >= (const Integer &) const;

    static int Compare(const Integer &, const Integer &);
    static void Add(const Integer &, const Integer &, Integer &);
    static void Sub(const Integer &, const Integer &, Integer &);
    static void Mul(const Integer &, const Integer &, Integer &); 
    static void Div(const Integer &, const Integer &, 
                                   Integer & Quo, Integer & Rem);
    static void Mod(const Integer &, const Integer &, Integer &);
    static void Exp(const Integer &, const Integer &, Integer &);
    // Computes A ^ B mod C
    static void ExpMod(const Integer &, const Integer &, 
                                     const Integer &, Integer &);

protected:
    void _Alloc(unsigned);
    void _ReAlloc(unsigned);
    void _DataShiftLeft(unsigned);
    bool _IsMax() const;
    bool _IsMin() const;
    void _FixSigBit();

    friend std::ostream & operator << (std::ostream & Str, 
                                            const Integer & Int);
};

Points of Interest

If you read the source code, the implementation of multiplication is strange and recursive, which is faster than the typical O(n^2) implementation of big multiplication of two integers of size n. In order to speed things up, I used some fancy formulas to replace a big multiplication into a few smaller multiplications, a few additions, and some fast bit shifts. For example, let's multiply 981 and 1234.

where,

981 * 1234 = (10^2*w + x) * (10^2*y+z),
p = w * y = 09 * 12,
q = x * z = 81 * 34,
r = (w * x) * (y + z) = 90 * 46 = 4140,

so that,

981 * 1234 = 10^4*p + 10^2(r - p - q) 
              + q = 10800000 + 127800 + 2754 = 1210554

Instead of separating parameters by decimal place, I separate them by half the number of double words they take up.

(The idea is from Fundamentals of Algorithmetrics, by Gilles Brassard and Paul Bratley.)

History

  • v.90:

    First submission, waiting for the slings and arrows from other members so I can improve the class.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Cap'n Code
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralBug FixmemberAndrew Phillips9 Feb '11 - 13:33 
I have been using this for years in HexEdit (see HexEdit - Window Binary File Editor[^]) and it works great (though not using all the features).
 
I recently encountered a small bug and provide here a fix. On line 440 of BigInteger.cpp change this line:
 
	i /= (sizeof(Unit) * 8);
 
to this:
 
	i /= (int)(sizeof(Unit) * 8);
 
This fix can also be seen in the public HexEdit SVN repository at https://hexedit1.svn.sourceforge.net/svnroot/hexedit1/trunk/HexEdit[^] in revision 5.
Andrew Phillips
http://www.hexedit.com
andrew @ hexedit.com

NewsWorks fine for me in HexEdit 3.2memberAndrew Phillips7 Mar '07 - 13:20 
Hi Cap'n,
 
I know you recommend not using this code but I used it in my hex editor before I saw your warning. I have thoroughly tested the results and have encountered no problem, at least with the way that I am using it. I also tried some other "big integer" classes but they were much slower.
 
I use the class in HexEdit for supporting the C# Decimal type in the "Properties" dialog. (This allows people to view and modify C# Decimal values which are 96 bit integers plus a small exponent.) To do this I need integers bigger than __int64 provides.
 
You can download the latest HexEdit from http://hexedit.com/Downloads/HexEd3_2beta1.zip if you want to try it. HexEdit is shareware (but there is also a free version). I can send you an activation code if you want to continue to use it. Please send email to aphillips@hexedit.com.
 
Note that I have acknowledged your contribution in the About box. If you click on the entry in the list it even opens a browser in this CodeProject page.
 
I just wanted to say thanks, and it works fine (for me).

 
Andrew Phillips
aphillips @ hexedit.com

GeneralFinal HexEdit 3.2 (hex editor) releasedmemberAndrew Phillips7 Apr '08 - 18:18 
Hello Cap'n Code. If you read this I am just letting you know that the code is in the final release of HexEdit 3.2, and acknowledged in the About box and the help. (See http://www.hexedit.com for details.)
 
Andrew Phillips
http://www.hexedit.com
andrew @ hexedit.com

GeneralLicensing stuff...memberbradr219 Oct '06 - 5:27 
Hi Cap'n'code.
 
Firstly neat large int class. Very easy to use.
 
Second, if I use this stuff in a commercial, shareware or freeware project how do you expect to be credited? In the about box, in user documentation etc... and as who? Cap'n'Code?
 
Finally, I've modified it to compile under VC6 (couple of minor problems in the asm), do you want me to send it to you? If so how?
 
Brad

GeneralRe: Licensing stuff...memberCap'n Code21 Oct '06 - 18:36 
I'm sorry to disappoint you, but I am not maintaining the article or
the code, and I am sure there are bugs in it as you can tell from the
message board. The division algorithm is pretty terrible, because if I
remember correctly it's bitwise. I personally don't care about
licensing as much as I care about having people shoot themselves in
the foot with code I haven't touched in well over year. In fact I wish
I knew a better way to mark the article as broken.
 
In summary: you can use it, but I don't think you should.
QuestionHow large Numbers ?memberRedDragon2k8 Oct '05 - 1:42 
Hello,
 
can this Integer Class also handle numbers like 62^12 ?
AnswerRe: How large Numbers ?memberAndrew Phillips7 Mar '07 - 13:23 
62^12? Why not?
 
I think the limitation is available memory or possible something like an integer no larger than can be stored in 2^31 bytes, which is VERY big.
 

 
Andrew Phillips
aphillips @ expertcomsoft.com

GeneralCompare-BUG!memberkdn01003@student.mdh.se22 Nov '04 - 0:21 
First of all I suggest that you update the code, since the multiplication error is still there.
 
I found a new bug. Try this:
 

Integer a, b;
 
a == b; // crashes in Integer.cpp (line 444) due to an invalid index
a != b; // crashes in Integer.cpp (line 444) due to an invalid index

 

By the way. What algorithms are you using for division and multiplication?
GeneralMultiplication errormemberWilliam C Russell20 Sep '04 - 5:28 
Confused | :confused: There is a bug in the multiplication. As a test case, I used the RSA-576 Challenge Numbers that were cracked to see if the multiplication worked correctly, and found a bug. So instead of Multiply, I took the result and divided by the divisor. This worked fine. I think multiples the quotient and the divisor, and the result did not equal the original value. Here is my code snippet and output. Interestingly enough the higher 39 digits are correct.
 
---- Code here ----
#include "stdafx.h"
#include "atlstr.h"
#include "Integer.h"
using namespace std;
 
int _tmain()
{
CString XStr="188198812920607963838697239461650439807163563379417382700763356422988859715234665485319060606504743045317388011303396716199692321205734031879550656996221305168759307650257059";
CString YStr="472772146107435302536223071973048224632914695302097116459852171130520711256363590397527";
 
Integer X, Y, Q, R, NewX;
 
X.FromString(XStr,10); // Load X
Y.FromString(YStr,10); // Load Y
 
cout << "Solution for (Q=X/Y): " << endl;
Integer::Div(X,Y,Q,R);
cout << "X: " << X << endl;
cout << "Y: " << Y << endl;
cout << "Q: " << Q << endl;
cout << "R: " << R << endl << endl;
 
cout << "Recomputing (X=Q*Y): " << endl;
Integer::Mul(Q,Y,NewX);
cout << "X: " << NewX << endl;
 
if (X==NewX)
cout << "Original X == New X : Test Succeeded!" << endl;
else
cout << "Original X != New X : Test Failed!" << endl;
 
return 0;
}
 
---- Output ----
Solution for (Q=X/Y):
X: 188198812920607963838697239461650439807163563379417382700763356422988859715234665485319060606504743045317388011303396716199692321205734031879550656996221305168759307650257059
Y: 472772146107435302536223071973048224632914695302097116459852171130520711256363590397527
Q: 398075086424064937397125500550386491199064362342526708406385189575946388957261768583317
R: 0
 
Recomputing (X=Q*Y):
X: 188198812920607963838697239461650439807199444837935429657364607858962702648253864966144617285181838098710385932957365159631056296483919156814087348255042671500623249738092707
Original X != New X : Test Failed!

GeneralRe: Multiplication errormemberWilliam C Russell21 Sep '04 - 16:26 
I found the bug. There is a boundary error when using GetSigUnitLen(), because if the SigBit is 32, the result is 2. If SigBit is 33 it is also 2. So, since they are both equal, the original code selects the wrong "big" number 50% of the time. So, I modified the code to use the SigBit instead. BTW, I will probably use your code for a project I am doing. Is your code "Free" other than giving you credit?
Cool | :cool:
// Quick, because the CPU can help
if (Multiplier.SigBit <= 32 || Multiplicand.SigBit <= 32)
{
// Original code -- wcr
// Replaced because of boundary bug with GetSigUnitLen() -- wcr
// const Integer & Small = (Multiplicand.GetSigUnitLen() == 1) ? Multiplicand : Multiplier; // wcr

// Fixed code -- wcr
// Fixed to use Integer::SigBit for size comparison -- wcr
const Integer & Small = (Multiplicand.SigBit < Multiplier.SigBit) ? Multiplicand : Multiplier; // wcr fixed 9/21/04
 
const Integer & Big = (&Multiplicand == &Small) ? Multiplier : Multiplicand; // wcr

unsigned BLen = Big.GetSigUnitLen();

GeneralRe: Multiplication errormemberits-sick28 Dec '05 - 6:31 
thanks
 
I just found that bug myself and came here to report it.
Your fix works ok for now.
So thanks for saving me the trouble of doing it myeslf.
 
Itsik
 

GeneralMultiplication algorithmmemberTim Forsythe15 Jul '04 - 8:01 
My comments are on your multiplication algorithm. I have not looked at the code.
 
When writing multiplication and division algorithms - if you think of your numbers as hexadecimal digits you can perform the same operations on two digits by replacing the multiples of 100^2 with multiples of 2^8 = 256. This is usually faster to implement because rather than multiplying/dividing by multiples of 100, you can shift by bytes and/or words.
 
So you get (for multiplication):
 
result = p*2^16 + (r-p-q)*2^8 + q
 
I used a similar method over a decade ago while implementing multilication and division algorithms in Forth on small micros where speed was critical. I only suggest this because you indicate that you were trying to optimize for speed.
 
Hope this helps.
 
Tim Forsythe
GeneralI like the MathematicmemberHing14 Jul '04 - 15:58 
Dear,
 
So good, I like the "Points of Interest" parts. I do believe that mathematic algorithm is important to make difference to other ordinary program:
 
(The idea is from Fundamentals of Algorithmetrics, by Gilles Brassard and Paul Bratley.)
 
It would be appreciate if you can add more explanation to the mathematic algorithm ... Wink | ;)
 
Best regards,
Hing
GeneralAnother serious BUGsussHarCore7 Jul '04 - 1:52 
I found another serious BUG in your modular arithmetic.
For example:

9h ^ 20000000000000h (mod 9999999991h) = 9h ^ (1FFFFFFFFFFFFFh + 1h) (mod 9999999991h) = 9h ^ (1h) * 9h ^ (1FFFFFFFFFFFFFh) (mod 9999999991h)
 
9h ^ (1FFFFFFFFFFFFFh) (mod 9999999991h) = 76952A9FF4h
 
Then 9h ^ (1h) * 9h ^ (1FFFFFFFFFFFFFh) (mod 9999999991h) = 76952A9FF4h * 9h (mod 9999999991h) = ???

 
Real value must be 91A4E6062Eh, but in your realization this value is 5F82082853h Wink | ;) .
 


GeneralRe: Another serious BUGmemberCap'n Code7 Jul '04 - 11:22 
Fixed it. My code asplode.
 
Bit shift to the left, bit shift to the right.
Push stack, pop stack, byte, byte byte!
GeneralRe: Another serious BUGsussHarCore7 Jul '04 - 13:50 
Cap`n Code wrote:
Fixed it. My code asplode.
 
Excuse me, but BUG didn't fix fully.
 
See next example:

9999999999999992h ^ 0FFFFFFFFFFFFFFFFh (mod 0111111111111111h) =
9999999999999992h ^ (0FFFFFFFFFFFFFFF1h+0Eh) (mod 0111111111111111h) =
(9999999999999992h ^ 0FFFFFFFFFFFFFFF1h)*(9999999999999992h ^ 0Eh) (mod 0111111111111111h) = 4000h * 2h (mod 0111111111111111h) = ???

 
In your realization we get 0, but real value is 8000h Wink | ;)

GeneralRe: Another serious BUGsussHarCore7 Jul '04 - 14:14 
Another example

2h ^ 0FFh (mod 100000001h) = ???

Result must be 80000001h Wink | ;)
 
You can make optimization of the modular reduction realization if you realize Mohomery's or Barett's method (binary algorithm in your realization is slowly against my realization of Barett's method in 3-5 times).
GeneralRe: Another serious BUGmemberCap'n Code7 Jul '04 - 22:01 
I'll google that stuff, or perhaps you have a link? There's always a faster boat, until you get to that limit in computational complexity... you know the one.
 
Thanks for the help.
 
Bit shift to the left, bit shift to the right.
Push stack, pop stack, byte, byte byte!
GeneralRe: Another serious BUGmemberavsrivastava27 Aug '06 - 12:12 
correct the following line in the code
line no 1201:
Integer::Exp(Factor, Exponent, newVal);
 
line no 1238:
Integer::ExpMod(newVal, Exponent, Mod, Product);
 
line no 1247:
Integer::ExpMod(Factor, Exponent, Mod, newVal);
 
a typical copy paste bug.

QuestionSigBit ?memberKochise6 Jul '04 - 22:11 
What the hell are you using 'SigBit' to keep track of the bit of sign ? You use inline assembly, so you may know how processor works :/ Just do a Data[UnitLen-1]&0x8000000 to know the sign bit. Otherwise, what ALWAYS lack of any big integer wrapper is the CARRY BIT ! Handle it instead of the sign bit, and you'll make a happy man !
 
Kochise
 
PS : Just make a BOOL returning function to get the sign bit...
 
In Code we trust !
AnswerRe: SigBit ?memberCap'n Code7 Jul '04 - 9:22 
UnitLen keeps track of how much of Data[] is "allocated" (in quotes because Data[] is a fixed size array). When UnitLen > MaxSize, there is an overflow error. SigBit keeps track of how much space is used describing the integer, and the sign. A lot of operations need to know if an Integer is zero, and it is much faster to test if SigBit == 0 rather than check if Data[i] == 0 for i = UnitLen - 1 to 0.
 
But your idea isn't a bad one, and I'll keep in it mind.
 
Bit shift to the left, bit shift to the right.
Push stack, pop stack, byte, byte byte!
GeneralStack overflowsussHarCore6 Jul '04 - 15:19 
I've got stack overflow.

Integer A, X, Q, R;
...
// Set variable
// A = 9999999999999999999999999999999999999999h (HEX)
// X = 9999999999999999999999999999999999999999h (HEX)
// Q = 9999999999999999999999999999999999999991h (HEX)
...
Integer::ExpMod(A,X,Q,R); // < Stack overflow here.
 
// Result ( R ) must be 8000000h

 


GeneralRe: Stack overflowmemberCap'n Code6 Jul '04 - 16:10 
Another shameful bug. I fixed it, and the the code 'll be up soon.
 
Bit shift to the left, bit shift to the right.
Push stack, pop stack, byte, byte byte!
Generalpi-testmemberdbwz6 Jul '04 - 14:41 
I used two integers, the first consisting of the first 100 digits of pi and the second of the subsequent 100 digits of pi (available on request, or by googling it). The interesting result of EclidGCD is -1 (!?) whereas both integers are positive. Using another pair of integers, the first consisting of the first 300 digits of pi and the second consisting of the subsequent 300 digits of pi I get an integer overflow exception. I've got both pairs in files of course and give me a shout if you want them. FYI: I compiled the source into a console program using this:
 
cl -GX EclidGCD.cpp integer.cpp
 
/d
GeneralRe: pi-testmemberCap'n Code6 Jul '04 - 15:22 
I found the problem, and I'm uploading the new code. Thanks for your vigilance! And I found the bug really quickly.
 
Nice idea for testing too, because it's better than typing jibberish.
 
Bit shift to the left, bit shift to the right.
Push stack, pop stack, byte, byte byte!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 6 Jul 2004
Article Copyright 2004 by Cap'n Code
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid