Un desafío divertido. :)
Supongo que quieres números enteros de longitud arbitraria. Sugiero el siguiente enfoque:
Considere la naturaleza binaria del tipo de datos "int". Piense en usar operaciones binarias simples para emular lo que hacen los circuitos en su CPU cuando agregan cosas. En caso de que esté interesado en más profundidad, considere leer este artículo de wikipedia sobre medios sumadores y sumadores completos . Harás algo similar a eso, pero puedes bajar a un nivel tan bajo como ese, pero siendo vago, pensé en renunciar y encontrar una solución aún más simple.
Pero antes de entrar en detalles algorítmicos sobre sumar, restar, multiplicar, busquemos una estructura de datos. Una forma sencilla, por supuesto, es almacenar cosas en un std :: vector.
template< class BaseType >
class BigInt
{
typedef typename BaseType BT;
protected: std::vector< BaseType > value_;
};
Es posible que desee considerar si desea hacer el vector de un tamaño fijo y si preasignarlo. La razón es que para diversas operaciones, tendrá que pasar por cada elemento del vector - O (n). Es posible que desee saber de antemano qué tan compleja será una operación y una n fija hace precisamente eso.
Pero ahora a algunos algoritmos sobre cómo operar con números. Podrías hacerlo en un nivel lógico, pero usaremos esa potencia mágica de la CPU para calcular los resultados. Pero lo que tomaremos de la ilustración lógica de Half- y FullAdders es la forma en que trata con los acarreos. Como ejemplo, considere cómo implementaría el operador + = . Para cada número en BigInt <> :: value_, los agregaría y vería si el resultado produce alguna forma de acarreo. No lo haremos en términos de bits, sino que confíe en la naturaleza de nuestro BaseType (ya sea largo, int, corto o lo que sea): se desborda.
Seguramente, si suma dos números, el resultado debe ser mayor que el mayor de esos números, ¿verdad? Si no es así, el resultado se desbordó.
template< class BaseType >
BigInt< BaseType >& BigInt< BaseType >::operator += (BigInt< BaseType > const& operand)
{
BT count, carry = 0;
for (count = 0; count < std::max(value_.size(), operand.value_.size(); count++)
{
BT op0 = count < value_.size() ? value_.at(count) : 0,
op1 = count < operand.value_.size() ? operand.value_.at(count) : 0;
BT digits_result = op0 + op1 + carry;
if (digits_result-carry < std::max(op0, op1)
{
BT carry_old = carry;
carry = digits_result;
digits_result = (op0 + op1 + carry) >> sizeof(BT)*8;
}
else carry = 0;
}
return *this;
}
La otra operación aritmética es análoga. Demonios, incluso podrías usar los stl-functors std :: plus y std :: minus, std :: times y std :: divides, ..., pero cuidado con el carry. :) También puede implementar la multiplicación y la división usando sus operadores más y menos, pero eso es muy lento, porque eso volvería a calcular los resultados que ya calculó en llamadas anteriores a más y menos en cada iteración. Hay muchos buenos algoritmos para esta simple tarea, use wikipedia o la web.
Y, por supuesto, debe implementar operadores estándar como operator<<
(simplemente cambie cada valor en value_ a la izquierda para n bits, comenzando en value_.size()-1
... oh y recuerde el acarreo :), operator<
- incluso puede optimizar un poco aquí, verificando el número aproximado de dígitos con el size()
primero. Y así. Luego haga que su clase sea útil, befriendig std :: ostream operator<<
.
¡Espero que este enfoque sea útil!