Alters

BigInteger - Funciones básicas: Resta de BigInteger

Buenas!
Vamos a seguir con la documentación de la arquitectura BigInteger.
En esta ocasión, vamos a explicar la resta de dos BigInteger.

  • ¿No es la entrada que buscabas? Entra en el índice
  • ¿Buscas el código fuente? Aquí lo tienes


La resta de BigInteger funciona de manera parecida a la suma, pero con ciertas particularidades, como todas las funciones.

De nuevo, tenemos una función dedicada a realizar la resta dígito a dígito, una validación de datos, normalización y signum, y demás elementos ya vistos en la suma.

De hecho, la suma y la resta están íntimamente relacionadas, ya que existe interoperabilidad entre ambas funciones. Ya hemos visto que la suma de BigInteger puede desembocar en una resta... pues bien, una resta de BigInteger puede terminar en la suma de dos BigInteger.

Una vez hecho este pequeño preámbulo, vamos a entrar en materia.

Para empezar, la función de resta (sub(a, b)), almacena el resultado en el primer operando, es decir, "a = a - b".

Una vez se invoca a la función de resta, lo primero que se hace es inspeccionar la validez de los parámetros de entrada (dos BigInteger "a" y "b", que serán restados).

Acto seguido se crean dos BigInteger temporales, sobre los que se copian los parámetros de entrada, y se comparan entre ellos.

Si "b" es mayor que "a", se llama de nuevo a la misma función de resta, pero con los operandos cambiados. Esto se hace para agilizar la resta, ya que es más fácil calcular la resta a sabiendas que "b" siempre será inferior a "a".

Para esta apreciación, cabe clarificar que buscamos |a| > |b|, es decir, comparamos sus valores absolutos, por lo que es importante tener en cuenta el signum.

Se considera "a < b" cuando "a < b" y el signum es distinto de 11, o bien cuando "a > b" y el signum es 11.

Así pues, si se da el caso, realizamos la resta invirtiendo el orden de los operandos, y al finalizar la operación cambiamos el signo de "a"; por otra parte, si se cumple que "a = b", damos a "a" el valor "0".

Finalmente, si "a > b", realizamos la normalización de signos y operamos los dígitos.

En este caso, la normalización se efectúa de la siguiente manera:

  • Si el signum es 0: No se hace ningún cambio.
  • Si el signum es 1: Cambiamos el signo de "b".
  • Si el signum es 10: Cambiamos el signo de "a".
  • Si el signum es 11: Cambiamos el signo de "a" y "b".

Tras realizar la normalización, se procede a la operación dígito a dígito: si "a" y "b" comparten signum (0 u 11), se resta; en caso contrario, se suma (en este caso, se llama a la función de suma, no a la suma dígito a dígito).

Para terminar, si "b" tiene signo negativo (signum 10 u 11), se invierte el signo de "a"; éste se asigna a la salida, y se limpia la memoria temporal.

Como sucede con la suma, durante esta primera explicación conlleva explicar otras funciones. En este caso, hemos hablado de la comparación de BigInteger, pero vamos a dejarlo para más adelante, ya que la comparación tiene su propia entrada reservada.

Así, pasamos directamente a la resta dígito a dígito. Esta resta funciona de manera similar a la suma por dígitos, pero como siempre, con sus particularidades.

Para la resta dígito a dígito, primero se crean dos BigInteger temporales, y se copian los datos de entrada (los operandos) sobre los datos temporales.

Una vez realizado esto, se restan los datos comunes; en este caso, como forzamos que "a > b", no nos preocupamos por los datos no comunes, ya que siempre estarán en "a".

Debemos capturar el signo del último dígito (el más significativo) para gestionar el tipo de acarreo.

Tras realizar la resta de datos comunes, gestionamos el acarreo, copiamos el primer operando a la salida, y liberamos memoria.

La gestión de acarreo está dividida en dos opciones: si el resultado es un número positivo, el acarreo se hace usando un sistema similar al de la suma: recorremos todos los datos buscando datos a normalizar (en este caso, datos menores que 0), para sumarles 10 en ese caso; de nuevo, vamos realizando pasadas hasta que no se produzca ningún acarreo.

Por otra parte, si el resultado ha sido un número negativo, cada vez que encontremos un número negativo simplemente le invertimos el signo; esta operación solo se hace una vez.

Al finalizar el acarreo queda por hacer una operación pendiente: recontar los dígitos, ya que una resta puede hacer que el BigInteger resultante tenga menos cifras que el original.

Para recontar, simplemente vamos decrementando Biginteger.count hasta encontrar un dígito distinto de cero.

Como siempre, para permitir la visualización, os dejo una imagen explicativa del flujo



Con esto, tenemos el primer grupo de funciones explicadas. Ahora nos quedan las operaciones de segundo rango (multiplicación y división), y finalmente las de tercer rango (potencias y raíces).

Espero que os resulte instructivo.

Nos vemos!



No hay comentarios:

Publicar un comentario