Todo es una cuestión de almacenamiento adecuado y algoritmos para tratar los números como partes más pequeñas. Supongamos que tiene un compilador en el que int
solo puede ser de 0 a 99 y desea manejar números de hasta 999999 (solo nos preocuparemos por los números positivos aquí para mantenerlo simple).
Lo haces dando el número tres int
sy usando las mismas reglas que (deberías haber) aprendido en la escuela primaria para sumar, restar y otras operaciones básicas.
En una biblioteca de precisión arbitraria, no existe un límite fijo en la cantidad de tipos de base utilizados para representar nuestros números, simplemente cualquier memoria que pueda contener.
adición por ejemplo: 123456 + 78
:
12 34 56
78
-- -- --
12 35 34
Trabajando desde el extremo menos significativo:
- acarreo inicial = 0.
- 56 + 78 + 0 carry = 134 = 34 con 1 llevar
- 34 + 00 + 1 acarreo = 35 = 35 con 0 acarreo
- 12 + 00 + 0 acarreo = 12 = 12 con 0 carry
Esto es, de hecho, cómo la adición generalmente funciona en el nivel de bit dentro de su CPU.
La resta es similar (restando el tipo base y tomando prestado en lugar de acarreo), la multiplicación se puede hacer con adiciones repetidas (muy lento) o cruzados (más rápido) y la división es más complicada pero se puede hacer resta de los números involucrados (la división larga que habría aprendido de niño).
De hecho, he escrito bibliotecas para hacer este tipo de cosas usando las potencias máximas de diez que se pueden encajan en un número entero al cuadrado (para evitar el desbordamiento cuando se multiplican dos int
s juntos, como un int
ser de 16 bits limitado a 0 a través de 99 para generar 9801 (< 32768) cuando al cuadrado, o de 32 bits int
usando de 0 a 9999 para generar 99,980,001 (< 2147483648)) que facilitó en gran medida los algoritmos.
Algunos trucos a tener en cuenta.
1/Al agregar o multiplicar números, antes de asignar el espacio máximo necesario entonces reducir más adelante, si usted encuentra que es demasiado. Por ejemplo, agregar dos números de 100 dígitos (donde el dígito es un int
) nunca le dará más de 101 dígitos. Multiplicar un número de 12 dígitos por un número de 3 dígitos nunca generará más de 15 dígitos (suma los dígitos).
2/Para mayor velocidad, normalice (reduzca el almacenamiento requerido) los números solo si es absolutamente necesario - mi biblioteca lo tuvo como una llamada separada para que el usuario pueda decidir entre la velocidad y el almacenamiento.
3/La suma de un número positivo y negativo es una resta, y restar un número negativo es lo mismo que sumar el equivalente positivo.Puede guardar bastante código haciendo que los métodos de sumar y restar se llamen unos a otros después de ajustar los letreros.
4/Evitar restar números grandes de los pequeños ya que invariablemente termina con números como:
10
11-
-- -- -- --
99 99 99 99 (and you still have a borrow).
En cambio, restar 10 del 11, luego negarlo:
11
10-
--
1 (then negate to get -1).
Éstos son el comentarios (convertidos en texto) de una de las bibliotecas para las que tuve que hacer esto. El código en sí mismo, por desgracia, está protegido por derechos de autor, pero es posible que pueda seleccionar la información suficiente para manejar las cuatro operaciones básicas. Suponga en lo siguiente que -a
y -b
representan números negativos y a
y b
son números cero o positivos.
Para Además, si los signos son diferentes, el uso de la resta de la negación:
-a + b becomes b - a
a + -b becomes a - b
Para resta, si los signos son diferentes, utilizar además de la negación:
a - -b becomes a + b
-a - b becomes -(a + b)
También un manejo especial para asegurar que estamos restando números pequeños de grandes:
small - big becomes -(big - small)
Multiplicación utiliza matemáticas de nivel de entrada de la siguiente manera:
475(a) x 32(b) = 475 x (30 + 2)
= 475 x 30 + 475 x 2
= 4750 x 3 + 475 x 2
= 4750 + 4750 + 4750 + 475 + 475
La forma en que esto se logra consiste en extraer cada uno de los dígitos de 32 uno a la vez (hacia atrás) a continuación, utilizando añadir a calcular una valor que se agregará al resultado (inicialmente cero).
ShiftLeft
y ShiftRight
operaciones se utilizan para multiplicar o dividir rápidamente un LongInt
por el valor de ajuste (10 para matemáticas "reales"). En el ejemplo anterior, agregamos 475 a cero 2 veces (el último dígito de 32) para obtener 950 (resultado = 0 + 950 = 950).
Luego desplazamiento a la izquierda para obtener 475 4750 y desplazamiento a la derecha para obtener 32 3. Agregar 4750 a cero 3 veces para obtener 14.250 a continuación, añadir a consecuencia de 950 para obtener 15200.
desviación a la izquierda para llegar 4750 47500, desplazamiento a la derecha 3 para obtener 0. Desde la derecha se movió 32 es ahora cero, hemos terminado y, de hecho, 475 x 32 es igual a 15200.
División también es difícil, pero basado en la aritmética temprana (el "gazinta "método para" entra en "). Considere la siguiente división larga para 12345/27
:
457
+-------
27 | 12345 27 is larger than 1 or 12 so we first use 123.
108 27 goes into 123 4 times, 4 x 27 = 108, 123 - 108 = 15.
---
154 Bring down 4.
135 27 goes into 154 5 times, 5 x 27 = 135, 154 - 135 = 19.
---
195 Bring down 5.
189 27 goes into 195 7 times, 7 x 27 = 189, 195 - 189 = 6.
---
6 Nothing more to bring down, so stop.
Por lo tanto es 12345/27
457
con 6
resto. Verificar:
457 x 27 + 6
= 12339 + 6
= 12345
Esto se implementa mediante el uso de una variable de draw-down (inicialmente cero) para derribar los segmentos de 12345 de uno en uno hasta que es mayor o igual a 27.
Luego simplemente restamos 27 de eso hasta que llegamos por debajo de 27 - el número de restas se agrega al segmento en la línea superior.
Cuando no hay más segmentos que derribar, tenemos nuestro resultado.
Tenga en cuenta que estos son algoritmos bastante básicos. Hay formas mucho mejores de hacer aritmética compleja si sus números van a ser particularmente grandes. Puede buscar algo como GNU Multiple Precision Arithmetic Library - es sustancialmente mejor y más rápido que mis propias bibliotecas.
Tiene el desafortunado error ya que simplemente saldrá si se queda sin memoria (una falla bastante fatal para una biblioteca de propósito general en mi opinión) pero, si se puede mirar más allá de eso, es bastante bueno en Que hace.
Si no puede usarlo por razones de licencia (o porque no desea que su aplicación simplemente salga sin motivo aparente), al menos puede obtener los algoritmos desde allí para integrarlos en su propio código.
También encontré que los cuerpos en MPIR (una bifurcación de GMP) son más susceptibles a las discusiones sobre cambios potenciales, parecen un grupo más amigable con los desarrolladores.
Creo que cubriste "Solo quiero saber si alguien tiene o puede proporcionar una explicación muy detallada y simplificada de la aritmética de precisión arbitraria" MUY bien –
Una pregunta de seguimiento: ¿Es posible establecer/detectar transportes y desbordamientos? sin acceso a código de máquina? – SasQ