Big Numbers, zły wynik dodawania

0

Cześć :)
Mam do napisania program, który wczytuje duże liczby oraz wykonuje na nich podstawowe działania arytmetyczne,

  1. niestety przy dodawaniu wynik wyświetla o jeden znak za dużo, nie wiem gdzie leży problem,
  2. jak wrzucić przeładowania operatorów << i >> do pliku .cpp z pliki .h?
    i kod źródłowy:
    .h:
#ifndef bigNumbers_h
#define bigNumbers_h

#include <iostream>
#include <cstdio>

#define STARTSIZE 10

using namespace std;

class Number {
	int size, current;
	char *digits;

public:

	Number();
	~Number();
	Number(Number& other);

	friend ostream& operator<<(ostream & s, const Number & p1) {
		char tmpDigit;
		for (int i = 0; i < p1.current; i++) {
			tmpDigit = p1.digits[i] + '0';
			s << tmpDigit;
		}
		return s;
	}
	friend istream& operator>>(istream & s, Number & p1) {
		char tmpDig;

		do {
			tmpDig = s.get();
			p1.digits[p1.current] = tmpDig - '0';
			p1.current++;
			if ((p1.current%STARTSIZE) == 0)
				p1.reallocSize();
		} while (tmpDig != '\n');
		return s;
	}

	Number& operator=(const Number& p1);
	Number& operator+(const Number& other) const;

	void reallocSize();
}; 

.cpp:

#include <iostream>
#include <cstdio>
#include "bigNumbers.h"

using namespace std;

Number::Number() {
	current = 0;
	size = STARTSIZE;
	digits = new char[size];
}

Number::~Number() {
	delete[] digits;
}

Number::Number(Number& other) {
	this->size = other.size;
	current = other.current;
	for (int i = 0;i < current; i++)
		digits[i] = other.digits[i];
}

Number& Number::operator=(const Number& other) {
	if (this == &other)
		return *this;

	if (digits != NULL)
		delete[] digits;

	size = other.size;
	current = other.current;
	digits = new char[size];
	for (int i = 0; i < current; i++)
		digits[i] = other.digits[i];

	return *this;
}

Number& Number::operator+(const Number& other) const {
	static Number sum;
	if (sum.digits != NULL)
		delete[] sum.digits;
	int tmpCurrent = current, tmpOtherCurrent = other.current, tmpSum = 0;
	if (tmpCurrent == tmpOtherCurrent) {
		sum.current = current;
		sum.size = size;
		sum.digits = new char[sum.size];
		int last = (sum.current) - 1;

		for (int i = last; i >= 0;i--) {
			sum.digits[i] = digits[i] + other.digits[i] + tmpSum;
			if (sum.digits[i] > 9) {
				sum.digits[i] -= 10;
				tmpSum = 1;
			}
			else
				tmpSum = 0;
		}
		if (tmpSum == 1) {
			for (int i = sum.current; i > 0; i--) {
				sum.digits[i] = sum.digits[i - 1];
			}
			sum.digits[0] = tmpSum;
			sum.current++;
			if ((sum.current%STARTSIZE) == 0)
				sum.reallocSize();
		}
	}
	return sum;
}
void Number::reallocSize() {
		char *tmpDigits;
		int tmpSize = this->size;
		tmpDigits = new char[size];
		for (int i = 0; i < tmpSize;i++)
			tmpDigits[i] = digits[i];
		size = size + STARTSIZE;
		delete[] digits;
		digits = new char[size];
		for (int i = 0; i < tmpSize;i++)
			digits[i] = tmpDigits[i];
}
 

wynik dodawania:
d780d30815.png

source.cpp:

#include <iostream>
#include <cstdio>
#include "bigNumbers.h"

using namespace std;

int main() {
	
	Number first, second, third;
	cin >> first;
	cin >> second;
	third = first + second;

	cout << "third = first + second:" << endl << third << endl;
	system("pause");
	return 0;
}
1

Błąd jest w tej pętli:

do {
		tmpDig = s.get();
		p1.digits[p1.current] = tmpDig - '0';
		p1.current++;
		if ((p1.current%STARTSIZE) == 0)
			p1.reallocSize();
	} while (tmpDig != '\n'); 

Może przekształć ją na while() lub pomyśl jak zablokować w ostatnim przebiegu, gdy skończą się już dane do przekształcenia,
bo to w tym obiegu dodaje się jeszcze current++. Dla liczby 100 po wyjściu current = 4.

  1. jak wrzucić przeładowania operatorów < i >> do pliku .cpp z pliki .h?

tak:

 friend ostream& operator<<(ostream & s, const Number & p1); // to w klasie
/*--------------------------------------------------------------------------------------------------*/
ostream& operator<<(ostream & s, const Number & p1) { // to w cpp
	char tmpDigit;
	for (int i = 0; i < p1.current; i++) {
		tmpDigit = p1.digits[i] + '0';
		s << tmpDigit;
	}
	return s;
}

No i ten nieszczęsny

 #define STARTSIZE 10
// dlaczego nie 
const int STARTSIZE = 10; 

Odchodzi się od dyrektywy #define

Wystarczy dodać

 if (tmpDig != '\n')
			p1.current++;

i problem z głowy lub zamienić na pętlę

 while (s.get(tmpDig) && tmpDig != '\n')
	{
		p1.digits[p1.current] = tmpDig - '0';
		p1.current++;
		if ((p1.current%STARTSIZE) == 0)
			p1.reallocSize();
	}
3
  1. Zapoznaj się z inkrementacją, bo jej nie rozumiesz: http://4programmers.net/Forum/1101404
  2. Może lepiej użyj: vector<uint8_t> digit trzymaj jako liczby (nie cyfry) i odwrócone czyli najpierw leci ostatnia cyfra. Jeżeli zabraniają wam użycie vector to najpierw napisz odpowiednik osobno.
2

Po pierwsze debugger. Im szybciej się nauczysz go używać tym lepiej.
Przez analizę kodu nie wypatrzyłem przyczyny błędu. Jednak operatory strumieniowe powinny wyglądać tak:

friend ostream& operator<<(ostream & s, const Number & p1) {
        if (p1.current==0) {
             return s << '0';
        }
        for (int i = 0; i < p1.current; i++) {
            char tmpDigit = p1.digits[i] + '0';
            s << tmpDigit;
        }
        return s;
    }

    friend istream& operator>>(istream & s, Number & p1) {
        s >> std::ws; // wyczysc białe znaki
        int ch;
 
        p1.current = 0;
        while (isdigit(ch = s.get()))  {
            p1.addDigit(ch-'0');
        }
        if (s.good()) {
            s.unget();
            if (p1.current==0) { // report reading error
                 s.clear(ios::bad);
            }
        }
        return s;
    }

    Number::addDigit(int digit)
    {
          assert(digit>=0 && digit<=9);

          if (current==size) {
               resize(size+STARTSIZE);
          }
          digits[current++] = digit;
    }

    Number::resize(int newSize)
    {
          if (newSize == size)
                return;
          char *newDigits = new char [newSize];
          size = newSize;
          current = std::min(current, newSize);
          std::copy(digits, digits+current, newDigits);
          delete [] digits;
          digits = newDigits;
    }

Uwagi do kodu:

  1. złe nazwa nazwa zmienej current powinno się nazywać significantDigits.
  2. reallocSize wyciek pamięci (użyj mojego resize).
  3. operator dodawania działa tylko dla liczb o tej samej ilości cyfr znaczących,
  4. brak const dla copy constructor
  5. digits nigdy nie jest NULL
0

dziękuję bardzo za wszelką pomoc :) a dla liczb o innej długości dopiero będę robił, tylko nie wiedziałem co tutaj mam źle, więc nie robiłem dalszej części

7

Co to za pomysł, żeby operator+ zwracał referencję do zmiennej statycznej?

Przez takie pomysły doprowadzasz do wielokrotnej modyfikacji zmiennych w tym samym wyrażeniu lub odczytu niezainicjalizowanej pamięci. UB.
http://melpon.org/wandbox/permlink/VYDfi9ZjA5H3Bikh

Prowadzącemu należy się pouczenie, że jak się nie zna to może nie powinno się uczyć.

0

Może taki początek:

#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

class BigNumber
  {
   public:
   typedef uint8_t digit;
   typedef vector<digit> container;
   typedef container::iterator iterator;
   typedef container::const_iterator citerator;
   typedef container::const_reverse_iterator criterator;
   private:
   container digits;
   digit push(digit carry);
   void reserve(size_t size) { digits.reserve(size); }
   public:
   BigNumber() {}
   BigNumber(uint64_t val);
   size_t size()const { return digits.size(); }
   void swap(BigNumber &bn) { digits.swap(bn.digits); }
   ostream &out(ostream &sout)const;
   static BigNumber add(const BigNumber &a,const BigNumber &b);
   BigNumber &operator+=(const BigNumber &bn) { BigNumber tmp=add(*this,bn); swap(tmp); return *this; }
   friend BigNumber operator+(const BigNumber &a,const BigNumber &b) { return add(a,b); }   
   friend ostream &operator<<(ostream &sout,const BigNumber &bn) { return bn.out(sout); }   
  };

BigNumber::BigNumber(uint64_t val)
  {
   for(uint64_t next=0;val;val=next) push(val-10*(next=val/10));
  }

BigNumber::digit BigNumber::push(digit carry)
  {
   bool dec=(carry>=10);
   if(dec) carry-=10;
   digits.push_back(carry);
   return dec;
  }

BigNumber BigNumber::add(const BigNumber &a,const BigNumber &b)
  {
   BigNumber ret;
   ret.reserve(max(a.size(),b.size())+1);
   BigNumber::citerator ia=a.digits.begin(),ea=a.digits.end(),ib=b.digits.begin(),eb=b.digits.end();
   digit carry=0;
   while((ia!=ea)&&(ib!=eb)) carry=ret.push(carry+*(ia++)+*(ib++));
   while(ia!=ea) carry=ret.push(carry+*(ia++));
   while(ib!=eb) carry=ret.push(carry+*(ib++));
   if(carry) ret.push(carry);
   return ret;
  }
     
ostream &BigNumber::out(ostream &sout)const
  {
   if(!digits.size()) sout<<'0';
   for(criterator i=digits.rbegin();i!=digits.rend();++i) sout<<((uint16_t)*i);
   return sout;
  }

int main()
  {
   BigNumber a(1234567),b(9876543);
   cout<<"a="<<a<<endl;
   cout<<"b="<<b<<endl;
   cout<<"a+b="<<(a+b)<<endl;
   return 0;
  }

1 użytkowników online, w tym zalogowanych: 0, gości: 1