Thread: arbitrary precision and operator overloading

  1. #1
    Registered User
    Join Date
    Mar 2007
    Posts
    416

    arbitrary precision and operator overloading

    I'm working on an arbitrary precision decimal class. I have everything working so far, but when I try to overload the = operator weird stuff happens. I can assign values to another variable of the same class, but only once. After that the values start to become messed up. There's some examples of what I mean below. The values of dub1 + dub2 does not add up to an integer of 4, but 3 dub1's do from the previous = assignment above the ret2 = dub1 + dub2. If I do only the first assignment (they're in blue) then it works correctly. The addition and subtraction both work correctly given its not assigned to something twice. All the code is there in case anyone needs a reference. The important parts are in blue.

    Code:
    //this typedef is to be used inside the class _double only
    typedef unsigned int *__double;
     
    //templated class for _double
    //MAXSIZE is the maximum precision of the value of _double
    //the first element in the array is considered the integer
    //the numbers from position 1 to MAXSIZE are considered decimals
    template<int MAXSIZE> class _double
    {
        private:
            __double bigdouble;// = new int[MAXSIZE];
        
        public:
            _double(); //constructor
            _double(int i, int* d, int size); //secondary constructor
            ~_double(); //destructor
            
            void copy(_double a); //copies b to a
            
            //begin addition operator overloading
            friend _double operator + (const _double a, const _double b)
            {
                _double<MAXSIZE> ret; //return value
                int carry = 0;
                
                for(int i = MAXSIZE - 1; i >= 0; i--){
                    
                    ret.bigdouble[i] = ( a.bigdouble[i] + b.bigdouble[i] + carry ) % 10; //find digit for current position
                    carry = ( a.bigdouble[i] + b.bigdouble[i] + carry ) / 10; //find carry over digit
                }
                
                return ret;    
            } //end operator+
            
            friend _double operator * (const _double a, const _double b)
            {
                _double<MAXSIZE> ret;
                return ret;
            }
            
            friend _double operator - (const _double a, const _double b)
            {
                _double<MAXSIZE> ret;
                
                for(int i = MAXSIZE - 1; i >= 0; i--){
                    
                    if(a.bigdouble[i] >= b.bigdouble[i])
                        ret.bigdouble[i] = a.bigdouble[i] - b.bigdouble[i];
                    else if(a.bigdouble[i] < b.bigdouble[i]){
                        int j = i-1;
                        while(a.bigdouble[j] == 0){ j--; }
                        
                        a.bigdouble[j] = a.bigdouble[j] - 1;
                        
                        for(j=j+1; j <= i; j++){
                            a.bigdouble[j] += 10;
                            if(j != i) a.bigdouble[j] -= 1;
                        }
                        
                        ret.bigdouble[i] = a.bigdouble[i] - b.bigdouble[i];
                    }
                }
                return ret;
            }
            
            _double operator = (const _double a) const
            {
                _double<MAXSIZE> ret;
                for(int i = 0; i < MAXSIZE; i++)
                    ret.bigdouble[i] = a.bigdouble[i];
                return ret;
            }
            
            void print(); //print the value of _double
    };
    
    template<int MAXSIZE> _double<MAXSIZE>::_double()
    {
        bigdouble = new unsigned int[MAXSIZE]; //allocate required space
        
        //set the entire array to zero
        for(int i = 0; i < MAXSIZE; i++){
            //if(i >= MAXSIZE) break; //safe guard just in case
            bigdouble[i] = 0; //set to zero
        }
    } //end constructor
    
    template<int MAXSIZE> _double<MAXSIZE>::_double(int i, int d[], int size)
    {
        bigdouble = new unsigned int[MAXSIZE]; //allocate required space
        
        bigdouble[0] = i;
        //set the entire array to zero
    
        for(int i = 1; i < MAXSIZE; i++){
            
            if(i-1 < size)
                bigdouble[i] = d[i-1]; //set to zero
            else
                bigdouble[i] = 0;
        }
    } //end constructor
    
    template<int MAXSIZE> _double<MAXSIZE>::~_double(){ delete [] bigdouble; } //delete allocated space
    
    //prints all the digits that are stored in the array
    template<int MAXSIZE> void _double<MAXSIZE>::print()
    {
        cout<<bigdouble[0]<<"."<<endl;
        for(int i = 1; i < MAXSIZE; i++)
            cout<<bigdouble[i];
    }
    
    //copies the contents of a
    template<int MAXSIZE> void _double<MAXSIZE>::copy(_double a)
    {
        for(int i = MAXSIZE - 1; i >= 0; i--)
            bigdouble[i] = a.bigdouble[i];
    }
    
    
    
    //the main program
    
    int main()
    {
        int decimal[] = {3,4,7};
        _double<10> dub1(1,decimal,3);
        
        int decimal2[] = {2,6,9};
        _double<10> dub2(0,decimal2,3);
        
        dub1.print(); cout<<endl<<endl;
        dub2.print(); cout<<endl<<endl;
        
        _double<10> ret = dub1;// + dub1;
        _double<10> ret2 = (dub1 + ret) + ret;
        
        ret2 = dub1 + dub2;
        
        ret2.print();
        
        return 0;
    }
    
    
    //The output from above int main() code
    
    1.
    347000000
    
    0.
    269000000
    
    4.
    041000000
    Process returned 0 (0x0)   execution time : 0.016 s
    Press any key to continue.

  2. #2
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Operator= should modify the left-hand value and return *this, not create a local object and return that. It's not the returning that makes assignment work, the return value just lets you chain assignments.

    Code:
    //this typedef is to be used inside the class _double only
    typedef unsigned int *__double;
    Then why not move it into the private section of the class? And identifiers beginning with double underscores are reserved for compiler writers. Identifiers beginning with any number of underscores are bad taste, IMO, as the meaning they give you is that you shouldn't use them: they are compiler-specific identifiers only to be used for implementing the standard library and other compiler-specific things.

    Code:
    bigdouble = new unsigned int[MAXSIZE]; //allocate required space
    Since MAXSIZE is template parameter and therefore a compile-time constant, you could just use a normal array. (Unless you plan to use bignums that are measured in kilobytes, in which case you might conserve quite a lot of space by using a smaller data type, like char.)

    Code:
    void copy(_double a); //copies b to a
    
    //copies the contents of a
    template<int MAXSIZE> void _double<MAXSIZE>::copy(_double a)
    Which way is it then? In any case this just duplicates the assignment operator.
    Last edited by anon; 04-21-2009 at 01:43 PM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  3. #3
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Note that names that begin with an underscore followed by an uppercase letter, or that contain consecutive underscores, are reserved to the (compiler and standard library) implementation for any use. Names that begin with an underscore are reserved to the implementation for use as a name in the global and ::std namespaces.

    Quote Originally Posted by scwizzo
    I have everything working so far, but when I try to overload the = operator weird stuff happens.
    Why is your overloaded operator= declared const? The fundamental semantics of operator= is that it modifies the current object. On the other hand, a member function named print is unlikely to modify the current object, yet it is non-const. A weird take on const correctness, methinks

    Another thing that you can do is to implement the assignment versions of the various operators as well. You could then implement say, operator+= as a member function, then implement operator+ as a free function using operator+=. Your assignment operators (including the copy assignment operator) should return by reference, not by value.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  4. #4
    Registered User
    Join Date
    Mar 2007
    Posts
    416
    Blah, I've been away from programming for a while and I've forgotten a bit. Thanks for the help/suggestions, I'll change my code accordingly and report back.

  5. #5
    Registered User
    Join Date
    Mar 2007
    Posts
    416
    I was able to get it to work kind of, but I run in to runtime errors that I'm thinking are coming from buffer overflows. Problem is I'm not sure how to solve it. I fixed the things you guys mentioned about the underscores, returning *this, and also looked at a dozen or more examples. If I uncomment the two lines I get errors. The weird thing about the runtime errors is it runs all the way to the end of main() and then stops. I've put cout<<"done" statements just before return 0; and it displays them. The only thing I changed in main() was the cout statement. The rest of the class is the same except for the change from _double to bdouble. Anywho, here's the code and error(s).

    Code:
            bdouble & operator = (bdouble a)
            {
                if(this != &a){
                    //delete [] this->bigdouble;
                    //bigdouble = new unsigned int[MAXSIZE];
                    for(int i = 0; i < MAXSIZE; i++)
                        bigdouble[i] = a.bigdouble[i];
                }
                return *this;
            }
    
    //errors
    incompatible types in assignment of `unsigned int*' to `unsigned int[10]'

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    On which line is the error identified?

    By the way, instead of manually managing memory, you might want to make use of std::vector or std::tr1::array.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    The larch
    Join Date
    May 2006
    Posts
    3,573
    You don't seem to have a copy constructor. (BTW, if you did use something smarter over dynamically allocated array - even a stack array - you wouldn't need to write neither a copy constructor, assignment operator nor destructor.)

    Code:
                    //delete [] this->bigdouble;
                    //bigdouble = new unsigned int[MAXSIZE];
    Although this has nothing to do with the problem (that this->bigdouble may already be deleted by a copy), that code would be rather meaningless. You want to deallocate memory only to allocate the exact same amount of memory again!?
    Last edited by anon; 04-22-2009 at 01:05 AM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  8. #8
    Registered User
    Join Date
    Mar 2007
    Posts
    416
    Quote Originally Posted by anon View Post
    Although this has nothing to do with the problem (that this->bigdouble may already be deleted by a copy), that code would be rather meaningless. You want to deallocate memory only to allocate the exact same amount of memory again!?
    It was originally to remove anything there, but yea it was kind of pointless and that's why I commented it out.

    Laserlight, I actually started changing everything over to vectors after I posted that last night as I had the same thought. Managing everything myself became pretty difficult.

    As for the line that's causing the problem, I don't know. I've put cout statements all over the class and main() and every line is printed, it only crashes when it reaches return 0;. I'll see if changing things to vectors makes things better in the end (and hopefully fixes the run time errors).

  9. #9
    The larch
    Join Date
    May 2006
    Posts
    3,573
    that this->bigdouble may already be deleted by a copy
    Did you understand that. If you don't define a copy constructor but use it, the copy will point to the same dynamically allocated array. If either of them goes out of scope, the other is left with a dangling pointer since the destructor deletes the shared array. When the second one goes out of scope, its destructor attempts to delete the shared array again. All kinds of funny things (can) come out of this.

    This won't be a problem if you switch to std::vector, since that manages resource allocation, deallocation and copying for you.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

Popular pages Recent additions subscribe to a feed