bigint

Código comentado línea a línea y paso a paso:

Entregas: bigint.hpp, bigint.cpp. Flags: c++ -Wall -Wextra -Werror -std=c++98

Qué pide (en palabras normales)

Subject (enunciado original)
[EN]
Assignment name : bigint
Expected files : bigint.hpp, bigint.cpp
--------------------------------------------------------------------------------
In computer science a bignum is an object representing an arbitrary precision
number, this is useful when you want to store a number bigger than SIZE_MAX
without any loss of precision. This is often achieved by storing an array or a
string containing the different "parts" of the number.
Create a class called bigint that will store an arbitrary precision unsigned
integer.
Your class must support addition, comparison and "digitshift" (like bitshift but
instead of shifting the bits you will shift the digits in base 10, e.g.:
(42 << 3 == 42000) and (1337 >> 2 == 13)).
Your bigint must be printable with the << operator (in base 10) and the output
should not contain any leading zeros.
You will find a main in this directory that must work with your class

[SUBJECT MAIN]
#include "bigint.hpp"
#include <iostream>

int main()
{
    const bigint a(42);
    bigint b(21), c, d(1337), e(d);

    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
    std::cout << "c = " << c << std::endl;
    std::cout << "d = " << d << std::endl;
    std::cout << "e = " << e << std::endl;

    std::cout << "a + b = " << a + b << std::endl;
    std::cout << "(c += a) = " << (c += a) << std::endl;

    std::cout << "b = " << b << std::endl;
    std::cout << "++b = " << ++b << std::endl;
    std::cout << "b++ = " << b++ << std::endl;

    std::cout << "(b << 10) + 42 = " << ((b << 10) + 42) << std::endl;
    std::cout << "(d <<= 4) = " << (d <<= 4) << std::endl;
    std::cout << "(d >>= 2) = " << (d >>= (const bigint)2) << std::endl;

    std::cout << "a =" << a << std::endl;
    std::cout << "d =" << d << std::endl;

    std::cout << "(d < a) = " << (d < a) << std::endl;
    std::cout << "(d <= a) = " << (d <= a) << std::endl;
    std::cout << "(d > a) = " << (d > a) << std::endl;
    std::cout << "(d >= a) = " << (d >= a) << std::endl;
    std::cout << "(d == a) = " << (d == a) << std::endl;
    std::cout << "(d != a) = " << (d != a) << std::endl;
}

[MAIN]

#include "bigint.hpp"

int main(void)
{
	const bigint a(42);
	bigint b(21), c, d(1337), e(d);

	// base test
	std::cout << "a = " << a << std::endl;
	std::cout << "b = " << b << std::endl;
	std::cout << "c = " << c << std::endl;
	std::cout << "d = " << d << std::endl;
	std::cout << "e = " << e << std::endl;

	std::cout << "a + b = " << a + b << std::endl;
	std::cout << "a + c = " << a + c << std::endl;
	std::cout << "(c += a) = " << (c += a) << std::endl;

	std::cout << "b = " << b << std::endl;
	std::cout << "++b = " << ++b << std::endl;
	std::cout << "b++ = " << b++ << std::endl;

	// b = 23, b << 10 -> 23000000000 + 42 = 23000000042
	std::cout << "(b << 10) + 42 = " << ((b << 10) + 42) << std::endl;
	std::cout << "(d <<= 4) = " << (d <<= 4) << ", d: " << d << std::endl;
	std::cout << "(d >>= 2) = " << (d >>= (const bigint)2) << ", d: " << d << std::endl;

	std::cout << "a = " << a << std::endl; // a = 42
	std::cout << "d = " << d << std::endl; // d = 5348

	std::cout << "(d < a) = " << (d < a) << std::endl; // (d < a) = 0
	std::cout << "(d > a) = " << (d > a) << std::endl; // (d > a) = 1
	std::cout << "(d == d) = " << (d == d) << std::endl; // (d == d) = 1
	std::cout << "(d != a) = " << (d != a) << std::endl; // (d != a) = 1
	std::cout << "(d <= a) = " << (d <= a) << std::endl; // (d <= a) = 0
	std::cout << "(d >= a) = " << (d >= a) << std::endl; // (d >= a) = 1

	// extra
	bigint x(12345678); bigint y(5);
	std::cout << "(x << y) = " << (x << y) << ", x: " << x << ", y: " << y << std::endl;
	std::cout << "(x >>= y) = " << (x >>= y) << ", x: " << x << ", y: " << y << std::endl;
	std::cout << "(x >= y) = " << (x >= y) << ", x: " << x << ", y: " << y << std::endl;

	std::cout << "x= " << (x <<= 5) << ", y= " << (y <<= 12) << std::endl;
	std::cout << "(x >= y) = " << (x >= y) << ", x: " << x << ", y: " << y << std::endl;

	return (0);
}

4 piezas

1. this = "yo"

this = el bigint que ejecuta la función ("yo"). *this = yo mismo.

2. const al final = "solo miro"

Pregunta única: ¿cambia algo de mi número? No → const · Sí → sin const.

El main tiene const bigint a(42) → a a solo se le pueden pedir cosas const.

3. return *this = "me devuelvo a mí" → bigint &

bigint&	operator++() { return *this += 1; }   // me cambio y me devuelvo a mí

++b cambia b y te devuelve el mismo b; c += a cambia c y te devuelve el mismo c. Si ves return *this, delante va bigint &.

4. Devolver uno nuevo → bigint (sin &)

a + b no cambia ni a ni b: fabrica un número nuevo y te lo da → bigint sin &, y const porque no me cambia.

bigint función por función

Función¿Cambia algo?¿Qué devuelve?Firma
constructor vacío (vale 0)--bigint();
constructor con número--bigint(unsigned int n);
copia--bigint(const bigint &o);
destructor--~bigint();
=síyobigint &operator=(const bigint &o);
+nouno nuevobigint operator+(const bigint &o) const;
+=síyobigint &operator+=(const bigint &o);
++bsíyobigint &operator++();
b++síuno nuevo (copia de cómo era antes)bigint operator++(int);
<< >> (desplazar)nouno nuevobigint operator<<(const bigint &n) const;
<<= >>=síyobigint &operator<<=(const bigint &n);
== != < > <= >=nosí/nobool operator<(const bigint &o) const;
friend (a la izquierda está cout, no un bigint: no puede ser miembro, pero se escribe dentro de la clase)
cout << b-el mismo osfriend std::ostream &operator<<(std::ostream &os, const bigint &b);

friend = "no soy miembro, pero puedo leer s". Así no hace falta un str() para imprimir.

Ojo, hay dos << distintos: el de dentro de la clase desplaza dígitos (b << 3) y el de fuera imprime (cout << b).

El constructor con número no lleva explicit: así (b << 10) + 42 convierte el 42 en bigint solo.

Suma del cole

bigint&	operator+=(const bigint& o)
{
	std::string r;
	int i = s.size() - 1;
	int j = o.s.size() - 1;
	int carry = 0;
	while (i >= 0 || j >= 0 || carry)
	{
		int sum = carry;
		if (i >= 0)
			sum += s[i--] - '0';
		if (j >= 0)
			sum += o.s[j--] - '0';
		r.insert(r.begin(), char('0' + sum % 10));
		carry = sum / 10;
	}
	s = r;
	return *this;
}
bigint	operator+(const bigint& o) const { bigint t(*this); return t += o; }

Como en el cole: empiezas por el último dígito, sumas, el dígito lo pones delante del resultado y te llevas carry. Sigue mientras queden dígitos o te lleves algo.

La suma de verdad va en +=; + copia y llama a +=. Cristi lo hace al revés: la suma en + (con bigint r; r.s = "";) y += es return *this = *this + oth;. Las dos valen.

Desplazar dígitos

EjemploQué haceCuidado
42 << 3 → 42000añade k ceros: s.append(k, '0')si vale "0", no añadas nada (si no queda "000")
1337 >> 2 → 13quita k dígitos del final: s.erase(s.size() - k)si k es mayor o igual que el número de dígitos → "0"

k (cuántos) se saca del bigint con strtoul(n.s.c_str(), 0, 10).

Comparar: solo escribes 2

a == b   →  s == o.s
a <  b   →  si tienen distinta longitud: el más corto es menor
            si tienen la misma:          s < o.s

los otros 4 salen de esos 2:
a != b   =  !(a == b)
a >  b   =  b < a
a <= b   =  !(b < a)
a >= b   =  !(a < b)

¿Por qué mirar la longitud primero? Como texto, "9" > "10" (compara el 9 con el 1). Como número, 9 < 10.

Cuerpos

bigint()         : s("0") {}
bigint(n)        std::ostringstream os; os << n; s = os.str();
copia            : s(o.s) {}
=                s = o.s; return *this;
+                bigint t(*this); return t += o;
++b              return *this += 1;
b++              bigint t(*this); ++(*this); return t;
<<= >>=          cambian s y return *this;
<<  >>           bigint t(*this); return t <<= n;   (o >>=)
cout << b        friend: return os << b.s;

Checklist