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)
- Un número enorme y sin signo (nunca negativo), más grande de lo que cabe en un
int. - Lo guardas como texto: el número 1337 es el string
"1337". - Sumar:
42 + 21 = 63. - Comparar:
==,!=,<,>,<=,>=. - Desplazar dígitos:
42 << 3 = 42000(añade 3 ceros) ·1337 >> 2 = 13(quita 2 dígitos). - Imprimir con
cout << b, sin ceros delante.
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í | yo | bigint &operator=(const bigint &o); |
+ | no | uno nuevo | bigint operator+(const bigint &o) const; |
+= | sí | yo | bigint &operator+=(const bigint &o); |
++b | sí | yo | bigint &operator++(); |
b++ | sí | uno nuevo (copia de cómo era antes) | bigint operator++(int); |
<< >> (desplazar) | no | uno nuevo | bigint operator<<(const bigint &n) const; |
<<= >>= | sí | yo | bigint &operator<<=(const bigint &n); |
== != < > <= >= | no | sí/no | bool 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 os | friend 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
| Ejemplo | Qué hace | Cuidado |
|---|---|---|
42 << 3 → 42000 | añade k ceros: s.append(k, '0') | si vale "0", no añadas nada (si no queda "000") |
1337 >> 2 → 13 | quita 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
- los 4 includes en el .hpp:
<iostream><string><sstream><cstdlib>· el .cpp solo incluye el .hpp - constructor vacío →
"0", no"" - resultado de la suma empieza vacío (
std::string r;· en cristir.s = "";) <<con"0"no añade ceros ·>>que se pasa →"0"<mira la longitud antes que el texto