Operar con números grandesCómo realizar operaciones con números enteros demasiados grandes que ni el propio .NET puede realizar |
|
El otro día estaba intentando hacer operaciones con números demasiado grandes (de por ejemplo 97 cifras cada uno) pero resulta que si bien el .NET puede realizarlas, al final acaba haciéndolo con valores de tipo Double y usando notación científica, con lo que no me servía ya que necesitaba que todos los dígitos fuesen válidos y estuviesen a la vista... al final, acabé maldiciendo al .NET y la falta de previsión de ese tipo de operaciones.
Buscando por la Web me encontré con programas especializados (y en la mayoría de los casos, con unos precios descomunales), lo único válido que encontré, además del amzi! Prolog, fue la aplicación "bc GNU" (bc - An arbitrary precision calculator language) una aplicación que permite realizar ciertas operaciones que no nos limitan el número de dígitos, además de que también podemos indicar si queremos o no usar decimales, este lenguaje "calculador" tiene ciertas instrucciones como la de realizar bucles for o bucles while de forma más o menos fácil, al menos para mi, que eso de Prolog me resultaba algo "enrevesado" para hacer ciertas cosas "simples".
La cuestión es que para los cálculos estoy usando bc, pero me dije que sería interesante poder realizar ese tipo de cálculos en .NET (e incluso en VB6).
Y aquí está lo que por ahora he hecho con esto de los números enteros de cualquier cantidad de dígitos:
-Sumar dos números (o varios) enteros de cualquier cantidad de dígitos,
-Multiplicar dos (o varios) números
-Realizar exponenciaciones (elevar un número a cualquier otro)
-Restar dos números (o varios)Por ahora no me he puesto a usar números con fracciones decimales ni a realizar la división, para eso necesitaré la ayuda de algún "matemático" que me explique cómo hacer ese tipo de operaciones trabajando individualmente con los dígitos que componen el número... ya que de eso se trata: de ir operando con cada uno de los dígitos de forma individual... tal y como lo hacemos manualmente.
En fin... si alguien se anima... pues podríamos hacer algo que realmente sea útil...
Mi idea será, en principio, crear un tipo de datos (una estructura) en C# en la que poder crear sobrecargas de operadores y conversiones a otros tipos de datos, cuando la versión 2005 de VB esté más avanzada, convertiré ese código a VB ya que en la versión 2005 se podrán crear sobrecargas de operadores y demás cosillas que actualmente sólo se pueden hacer con C#.
Aquí te pongo el código para VB .NET 2003 (será fácil convertirlo a la versión 2002, ya que realmente sólo hago uso de la declaración de variables en los bucles).
También pondré el código de C# y el código para VB6, así que... sigue comprobando esta página ya que seguramente habrá novedades pronto... al menos eso espero.
Para probar el código (y los resultados)
Para que puedas probar desde la línea de comandos, el proyecto (de consola) recibe argumentos de la línea de comandos, esos argumentos serán los dos números con los que operar y la operación a realizar.
Es decir, se indicarán los números con los que se deben operar y después la operación a realizar (a esto se le llama notación Polaca, por si te interesa saberlo).
Las operaciones podrán ser: * multiplicar, + sumar, - restar y ** elevar a una potencia, por ejemplo:
multStr 1234567890123456789012345 123456789 *
multStr 125 500 **Por supuesto, para comprobar que todas estas operaciones devuelven un resultado correcto tendrás que usar algún programa que sea capaz de realizarlas!!!
En el caso de 125 elevado a 500... ¡¡¡el número tendrá 1049 dígitos!!!Para comprobar los resultados, también puedes usar GNU bc, (desde la lista de referencias puedes bajar el ejecutable, la documentación y el código), aunque para usarla debes crear un fichero que realice las operaciones que quieres realizar y después llamar a bc.exe, por ejemplo, puedes crear un fichero llamado "pruebas.bc" con el siguiente contenido:
res = 1234567890123456789012345 * 123456789
print res, "\n\n"res = 125 ^ 500
print "125 ^ 500 = \n", res, "\n"quit
Y después usarla de esta forma:
bc pruebas.bc
Las funciones incluidas (los tipos de operaciones soportadas)
Aunque en este primer intento, (que hice anoche), todo el código está en el mismo módulo, al menos te indicaré las funciones que tiene y para que sirven, así, si quieres modificarlo, sepas de forma más fácil cómo funciona.
En algunas funciones se permiten dos parámetros o un array de varios parámetros, por ejemplo, si quieres multiplicar dos números puedes llamar a la función Mult2(n1, n2) indicando los dos números a multiplicar, pero si quieres realizar varias operaciones de multiplicación, puedes llamar a Mult2(n1, n2, n3, ... nN).
cada función se indicará seguida de /2 si recibe 2 parámetros y/o de /N si recibe N parámetros. Esta es una notación que usa en Prolog para indicar el número de parámetros que recibe cada función, y como me gusta...
Todas las funciones devuelven una cadena con el resultado.
- Mult2 (Mult2/2 y Mult2/N)
Multiplica dos (o N números).- PotN (PotN/2)
Eleva el primer número a la potencia indicada (realmente multiplica N veces el primer número indicado)- Resta2 (Resta2/2 y Resta2/N)
Resta 2 números o N números- Suma2 (suma2/2 y Suma2/N)
Suma 2 o más números- Div2 (Div2/2) (rev 0.1)
Divide dos números- Sobrecargado los operadores *, /, +, -, >, <, ==, != (rev 0.2)
Esto sólo para la versión de C#- Sobrecargado los operadores ++, -- (rev 0.3, por publicar)
Esto sólo para la versión de C#Internamente se usan las siguientes funciones y variables:
- ad() un array con cada uno de los números con los que hay que operar
Mult2 calcula cada una de las líneas y las guarda en el array ad() que posteriormente se sumará para conseguir el resultado.- swap cambia el contenido de los dos parámetros indicados, los parámetros se pasarán por referencia. Este método lo uso para poner primero el número más largo y después el más corto.
- sumarFilas, suma el contenido del array ad() (cada uno de los elementos) y devuelve el resultado.
- restar2Filas, resta los dos primeros elementos del array ad() y devuelve el resultado.
Nos vemos.
Guillermo
- El manual de GNU bc - An arbitrary precision calculator language
http://www.gnu.org/software/bc/manual/html_mono/bc.html- La versión de GNU bc para Windows:
http://gnuwin32.sourceforge.net/packages/bc.htm- El ejecutable de GNU bc para Windows:
http://prdownloads.sourceforge.net/gnuwin32/bc-1.06-bin.zip?download- El link al código, binario, documentación, etc. de GNU bc para Windows
http://sourceforge.net/project/showfiles.php?group_id=23617&release_id=50828
Si quieres, puedes participar en este proyecto, creo que puede ser interesante.
La idea es conseguir hacer cálculos con números bastantes grandes (de muchos dígitos) y por supuesto hacerlo lo más rápido posible, ya que, por ejemplo, el código de elevar a potencias, es relativamente lento...Las cosas que tengo anotadas por hacer:
(revisa siempre la última actualización para saber que es lo que hay pendiente de hacer)
- Trabajar con decimales (hecho rev 0.1)
- Dividir números (hecho rev 0.1)
- Calcular la raíz cuadrada
- Trabajar con números negativos
- ...lo que se me ocurra o lo que propongas...
Así que si te animas... me mandas un mensaje a: mensaje @ elguille.info con el asunto "Participar en numeros grandes" y ya hablamos.
Gracias.
'------------------------------------------------------------------------------ ' Multiplicar números usando cadenas de caracteres (10/Ago/04) ' Realizar operaciones con números enteros de cualquier longitud ' Operaciones soportadas: ' Multiplicación, Potencias, Suma y Resta ' ' ©Guillermo 'guille' Som, 2004 '------------------------------------------------------------------------------ Option Explicit On Option Strict On Imports vb = Microsoft.VisualBasic Imports System Module MultStr Dim ad() As String ' Sub Main(ByVal args() As String) ' 'Console.WriteLine("125 ^ 9 = {0}", 125 ^ 9) 'Console.WriteLine() ' Dim num1 As String = "123" Dim num2 As String = "123" Dim op As String = "*" If args.Length > 1 Then num1 = args(0) num2 = args(1) End If If num2.Length > num1.Length Then swap(num1, num2) End If ' If args.Length = 3 Then op = args(2) End If ' Select Case op Case "+" Console.WriteLine(Suma2(num1, num2)) Case "-" Console.WriteLine(Resta2(num1, num2)) Case "**" Console.WriteLine(PotN(num1, num2)) Case "*", "x" Console.WriteLine(Mult2(num1, num2)) ' ' Para mostrar todas las cifras que intervienen ' en las operaciones, comenta esto si sólo quieres el resultado Dim res As String = Mult2(num1, num2) Dim k As Integer = res.Length Console.WriteLine(" {0," & k.ToString & "}", num1) Console.WriteLine("x {0," & k.ToString & "}", num2) Console.WriteLine(New String("-"c, k + 2)) For i As Integer = 0 To ad.Length - 1 Console.WriteLine(" {0," & k.ToString & "}", ad(i)) Next Console.WriteLine(New String("-"c, k + 2)) Console.WriteLine(" {0," & k.ToString & "}", res) End Select End Sub ' ' Public Function PotN(ByVal num1 As String, ByVal elevado As String) As String Dim i As Integer = CInt(elevado) - 1 Dim p(i) As String For k As Integer = 0 To i p(k) = num1 Next Return Mult2(p) End Function ' Public Function Mult2(ByVal ParamArray nums() As String) As String Dim total As String = nums(0) For i As Integer = 1 To nums.Length - 1 Dim n1 As String = nums(i) total = Mult2(total, n1) Next ' Return total End Function ' Public Function Mult2(ByVal num1 As String, ByVal num2 As String) As String ' ' Poner el más largo como primer número If num2.Length > num1.Length Then swap(num1, num2) End If ' ' El número de operaciones necesarias ' será la cantidad de cifras del más pequeño ReDim ad(num2.Length - 1) Dim n As Integer = -1 Dim resto As String = "0" Dim res As String = "" ' ' Multiplicar formando filas con los resultados (al estilo manual) For i As Integer = num2.Length - 1 To 0 Step -1 n += 1 ad(n) = "" ' Añadir espacios a la derecha según la cifra (fila) que se procese For k As Integer = 1 To n ad(n) &= " " Next Dim c1 As String = num2.Substring(i, 1) ' Para simplificar las cosas ' se comprueba si se multiplicará por ceo o por uno ' de forma que no sea necesario hacer estas operaciones If c1 = "0" Then ad(n) = New String("0"c, num1.Length) & ad(n) ElseIf c1 = "1" Then ad(n) = num1 & ad(n) Else For j As Integer = num1.Length - 1 To 0 Step -1 Dim c2 As String = num1.Substring(j, 1) res = (CInt("0" & c1) * CInt("0" & c2) + CInt(resto)).ToString ad(n) = vb.Right(res, 1) & ad(n) If res.Length - 1 < 1 Then resto = "0" Else resto = res.Substring(0, res.Length - 1) End If Next If resto <> "0" Then ad(n) = resto & ad(n) resto = "0" End If End If Next ' Return sumarFilas() End Function ' Public Function Suma2(ByVal ParamArray nums() As String) As String ' ReDim ad(nums.Length - 1) For i As Integer = 0 To nums.Length - 1 ad(i) = nums(i) Next ' Return sumarFilas() End Function ' Public Function Suma2(ByVal num1 As String, ByVal num2 As String) As String ReDim ad(1) ad(0) = num1 ad(1) = num2 ' Return sumarFilas() End Function ' Public Function Resta2(ByVal ParamArray nums() As String) As String Dim total As String = nums(0) For i As Integer = 1 To nums.Length - 1 Dim n1 As String = nums(i) total = Resta2(total, n1) Next ' Return total End Function ' Public Function Resta2(ByVal num1 As String, ByVal num2 As String) As String ReDim ad(1) ad(0) = num1 ad(1) = num2 ' Return restar2Filas() End Function ' Private Function sumarFilas() As String Dim m As Integer = 0 Dim n As Integer = ad.Length - 1 ' ' m será el número de mayor longitud For k As Integer = 0 To n If ad(k).Length > m Then m = ad(k).Length Next ' ' sumar las filas obtenidas Dim resto As String = "0" Dim total As String = "" Dim res As String = "" ' For k As Integer = 0 To n ad(k) = vb.Right(New String(" "c, m) & ad(k), m) Next For k As Integer = m - 1 To 0 Step -1 res = resto For i As Integer = 0 To n res = (CInt(res) + CInt("0" & ad(i).Substring(k, 1))).ToString Next total = vb.Right(res, 1) & total If res.Length - 1 < 1 Then resto = "0" Else resto = res.Substring(0, res.Length - 1) End If Next If resto <> "0" Then total = resto & total End If ' Return total End Function ' Private Function restar2Filas() As String Dim m As Integer = 0 Dim n As Integer = ad.Length - 1 ' For k As Integer = 0 To n If ad(k).Length > m Then m = ad(k).Length Next ' ' restar las filas obtenidas Dim resto As String = "0" Dim total As String = "" Dim res As String = "" ' For k As Integer = 0 To n ad(k) = vb.Right(New String(" "c, m) & ad(k), m) Next For k As Integer = m - 1 To 0 Step -1 res = (CInt("0" & ad(0).Substring(k, 1)) - (CInt("0" & ad(1).Substring(k, 1)) + CInt(resto))).ToString If CInt(res) < 0 Then res = (CInt(res) + 10).ToString End If total = vb.Right(res, 1) & total If res.Length - 1 < 1 Then resto = "0" Else resto = res.Substring(0, res.Length - 1) End If Next If resto <> "0" Then total = resto & total End If ' Return total End Function ' Private Sub swap(ByRef n1 As String, ByRef n2 As String) Dim t As String = n1 n1 = n2 n2 = t End Sub End Module
//----------------------------------------------------------------------------- // Multiplicar números usando cadenas de caracteres (10/Ago/04) // Realizar operaciones con números enteros de cualquier longitud // // Operaciones soportadas: // Multiplicación, Potencias, Suma y Resta // // ©Guillermo 'guille' Som, 2004 //----------------------------------------------------------------------------- using System; class MultStr{ private static string[] ad; // [STAThread] static void Main(string[] args) { // string num1 = "123"; string num2 = "123"; string op = "*"; if( args.Length > 1 ){ num1 = args[0]; num2 = args[1]; } if( num2.Length > num1.Length ){ swap(ref num1, ref num2); } // if( args.Length == 3 ){ op = args[2]; } // switch(op){ case "+": Console.WriteLine(Suma2(num1, num2)); break; case "-": Console.WriteLine(Resta2(num1, num2)); break; case "**": Console.WriteLine(PotN(num1, num2)); break; case "*": case "x": Console.WriteLine(Mult2(num1, num2)); // // Para mostrar todas las cifras que intervienen string res = Mult2(num1, num2); int k = res.Length; Console.WriteLine(" {0," + k.ToString() + "}", num1); Console.WriteLine("x {0," + k.ToString() + "}", num2); Console.WriteLine(new string('-', k + 2)); for(int i= 0; i<= ad.Length - 1; i++){ Console.WriteLine(" {0," + k.ToString() + "}", ad[i]); } Console.WriteLine(new string('-', k + 2)); Console.WriteLine(" {0," + k.ToString() + "}", res); break; } // Console.ReadLine(); } // // public static string PotN(string num1, string elevado) { int i = Convert.ToInt32(elevado) - 1; string[] p = new string[(i + 1)]; for(int k= 0; k<= i; k++){ p[k] = num1; } return Mult2(p); } // public static string Mult2( params string[] nums) { string total = nums[0]; for(int i= 1; i<= nums.Length - 1; i++){ string n1 = nums[i]; total = Mult2(total, n1); } // return total; } // public static string Mult2(string num1, string num2) { // // Poner el más largo como primer número if( num2.Length > num1.Length ){ swap(ref num1, ref num2); } // // El número de operaciones necesarias // será la cantidad de cifras del más pequeño ad = new string[num2.Length]; int n = -1; string resto = "0"; string res = ""; // // Multiplicar formando filas con los resultados (al estilo manual) for(int i= num2.Length - 1; i>= 0; i--){ n += 1; ad[n] = ""; // Añadir espacios a la derecha según la cifra (fila) que se procese for(int k= 1; k<= n; k++){ ad[n] += " "; } string c1 = num2.Substring(i, 1); // Para simplificar las cosas // se comprueba si se multiplicará por ceo o por uno // de forma que no sea necesario hacer estas operaciones if( c1 == "0" ){ ad[n] = new string('0', num1.Length) + ad[n]; }else if( c1 == "1" ){ ad[n] = num1 + ad[n]; }else{ for(int j= num1.Length - 1; j>= 0; j--){ string c2 = num1.Substring(j, 1); res = (Convert.ToInt32("0" + c1) * Convert.ToInt32("0" + c2) + Convert.ToInt32(resto)).ToString(); ad[n] = res.Substring(res.Length - 1, 1) + ad[n]; if( res.Length - 1 < 1 ){ resto = "0"; }else{ resto = res.Substring(0, res.Length - 1); } } if( resto != "0" ){ ad[n] = resto + ad[n]; resto = "0"; } } } // return sumarFilas(); } // public static string Suma2( params string[] nums) { // ad = new string[nums.Length]; for(int i= 0; i< nums.Length; i++){ ad[i] = nums[i]; } // return sumarFilas(); } // public static string Suma2(string num1, string num2) { ad = new string[2]; ad[0] = num1; ad[1] = num2; // return sumarFilas(); } // public static string Resta2( params string[] nums) { string total = nums[0]; for(int i= 1; i< nums.Length; i++){ string n1 = nums[i]; total = Resta2(total, n1); } // return total; } // public static string Resta2(string num1, string num2) { ad = new string[2]; ad[0] = num1; ad[1] = num2; // return restar2Filas(); } // private static string sumarFilas() { int m = 0; int n = ad.Length - 1; // // m será el número de mayor longitud for(int k= 0; k<= n; k++){ if( ad[k].Length > m ) m = ad[k].Length; } // // sumar las filas obtenidas string resto = "0"; string total = ""; string res = ""; // for(int k= 0; k<= n; k++){ ad[k] = (new string(' ', m) + ad[k]).Substring(ad[k].Length, m); } for(int k= m - 1; k>= 0; k--){ res = resto; for(int i= 0; i<= n; i++){ res = (Convert.ToInt32(res) + Convert.ToInt32("0" + ad[i].Substring(k, 1))).ToString(); } total = res.Substring(res.Length - 1, 1) + total; if( res.Length - 1 < 1 ){ resto = "0"; }else{ resto = res.Substring(0, res.Length - 1); } } if( resto != "0" ){ total = resto + total; } // return total; } // private static string restar2Filas() { int m = 0; int n = ad.Length - 1; // for(int k= 0; k<= n; k++){ if( ad[k].Length > m ) m = ad[k].Length; } // // restar las filas obtenidas string resto = "0"; string total = ""; string res = ""; // for(int k= 0; k<= n; k++){ ad[k] = (new string(' ', m) + ad[k]).Substring(ad[k].Length, m); } for(int k= m - 1; k>= 0; k--){ res = (Convert.ToInt32("0" + ad[0].Substring(k, 1)) - (Convert.ToInt32("0" + ad[1].Substring(k, 1)) + Convert.ToInt32(resto))).ToString(); if( Convert.ToInt32(res) < 0 ){ res = (Convert.ToInt32(res) + 10).ToString(); } total = res.Substring(res.Length - 1, 1) + total; if( res.Length - 1 < 1 ){ resto = "0"; }else{ resto = res.Substring(0, res.Length - 1); } } if( resto != "0" ){ total = resto + total; } // return total; } // private static void swap(ref string n1, ref string n2) { string t = n1; n1 = n2; n2 = t; } }
El código para Visual Basic 6.0 (o anteriores a VB .NET)
El formulario de prueba (y una captura en tiempo de ejecución)
El formulario en tiempo de ejecución.
'------------------------------------------------------------------------------ ' Realizar operaciones con números grandes usando cadenas (10/Ago/04) ' Formulario de prueba para Visual Basic 6.0 ' ' Realizar operaciones con números enteros de cualquier longitud ' Operaciones soportadas: ' Multiplicación, Potencia, Suma y Resta ' ' ©Guillermo 'guille' Som, 2004 '------------------------------------------------------------------------------ Option Explicit Private Sub cmdCalcular_Click() Select Case Left$(cboOp.Text, 1) Case "+" txtRes.Text = Suma2(txtNum1.Text, txtNum2.Text) Case "-" txtRes.Text = Resta2(txtNum1.Text, txtNum2.Text) Case "*" txtRes.Text = Mult2(txtNum1.Text, txtNum2.Text) Case "^" txtRes.Text = PotN(txtNum1.Text, txtNum2.Text) End Select End Sub Private Sub Form_Load() With cboOp .Clear .AddItem ("+ - Sumar") .AddItem ("- - Restar") .AddItem ("* - Multiplicar") .AddItem ("^ - Elevar a potencia") .ListIndex = 2 End With txtNum1.Text = "1234567890123456789012345" txtNum2.Text = "12345678901234567890" txtRes.Text = "" End Sub Private Sub Form_Resize() If WindowState <> vbMinimized Then txtNum1.Width = ScaleWidth - txtNum1.Left - 240 txtNum2.Width = txtNum1.Width cmdCalcular.Left = ScaleWidth - cmdCalcular.Width - 240 txtRes.Width = txtNum1.Width txtRes.Height = ScaleHeight - txtRes.Top - 240 End If End Sub
El código del módulo con las funciones
'------------------------------------------------------------------------------ ' Módulo con las funciones para realizar los cálculos (10/Ago/04) ' Versión para Visual Basic 6.0 ' ' Realizar operaciones con números enteros de cualquier longitud ' Operaciones soportadas: ' Multiplicación, Potencia, Suma y Resta ' ' ©Guillermo 'guille' Som, 2004 '------------------------------------------------------------------------------ Option Explicit Private ad() As String Public Function PotN(ByVal num1 As String, ByVal elevado As String) As String Dim i As Long Dim p() As String Dim k As Long ' i = CLng(elevado) - 1 ReDim p(i) For k = 0 To i p(k) = num1 Next PotN = Mult2N(p) End Function ' Public Function Mult2N(ParamArray nums() As Variant) As String Dim total As String Dim n1 As String Dim i As Long ' If IsArray(nums(0)) Then total = nums(0)(0) For i = 1 To UBound(nums(0)) n1 = nums(0)(i) total = Mult2(total, n1) Next Else total = nums(0) For i = 1 To UBound(nums) n1 = nums(i) total = Mult2(total, n1) Next End If ' Mult2N = total End Function ' Public Function Mult2(ByVal num1 As String, ByVal num2 As String) As String ' ' Poner el más largo como primer número If Len(num2) > Len(num1) Then Call swap(num1, num2) End If ' ' El número de operaciones necesarias ' será la cantidad de cifras del más pequeño ReDim ad(Len(num2) - 1) Dim n As Long n = -1 Dim resto As String resto = "0" Dim res As String ' ' Multiplicar formando filas con los resultados (al estilo manual) Dim i As Long For i = Len(num2) To 1 Step -1 n = n + 1 ad(n) = "" ' Añadir espacios a la derecha según la cifra (fila) que se procese Dim k As Long For k = 1 To n ad(n) = ad(n) & " " Next Dim c1 As String c1 = Mid$(num2, i, 1) ' Para simplificar las cosas ' se comprueba si se multiplicará por ceo o por uno ' de forma que no sea necesario hacer estas operaciones If c1 = "0" Then ad(n) = String$(Len(num1), "0") & ad(n) ElseIf c1 = "1" Then ad(n) = num1 & ad(n) Else Dim j As Long For j = Len(num1) To 1 Step -1 Dim c2 As String c2 = Mid$(num1, j, 1) res = CStr(CLng("0" & c1) * CLng("0" & c2) + CLng(resto)) ad(n) = Right$(res, 1) & ad(n) If Len(res) - 1 < 1 Then resto = "0" Else resto = Left$(res, Len(res) - 1) End If Next If resto <> "0" Then ad(n) = resto & ad(n) resto = "0" End If End If Next ' Mult2 = sumarFilas() End Function ' Public Function Suma2N(ParamArray nums() As Variant) As String Dim i As Long ' If IsArray(nums(0)) Then ReDim ad(UBound(nums(0))) For i = 0 To UBound(nums(0)) ad(i) = nums(0)(i) Next Else ReDim ad(UBound(nums)) For i = 0 To UBound(nums) ad(i) = nums(i) Next End If ' Suma2N = sumarFilas() End Function ' Public Function Suma2(ByVal num1 As String, ByVal num2 As String) As String ReDim ad(1) ad(0) = num1 ad(1) = num2 ' Suma2 = sumarFilas() End Function ' Public Function Resta2N(ParamArray nums() As Variant) As String Dim total As String Dim i As Long Dim n1 As String ' If IsArray(nums(0)) Then total = nums(0)(0) For i = 1 To UBound(nums(0)) n1 = nums(0)(i) total = Resta2(total, n1) Next Else total = nums(0) For i = 1 To UBound(nums) n1 = nums(i) total = Resta2(total, n1) Next End If ' Resta2N = total End Function ' Public Function Resta2(ByVal num1 As String, ByVal num2 As String) As String ReDim ad(1) ad(0) = num1 ad(1) = num2 ' Resta2 = restar2Filas() End Function ' Private Function sumarFilas() As String Dim i As Long Dim k As Long Dim m As Long Dim n As Long n = UBound(ad) ' ' m será el número de mayor longitud For k = 0 To n If Len(ad(k)) > m Then m = Len(ad(k)) Next ' ' sumar las filas obtenidas Dim resto As String resto = "0" Dim total As String Dim res As String ' For k = 0 To n ad(k) = Right$(String$(m, " ") & ad(k), m) Next For k = m To 1 Step -1 res = resto For i = 0 To n res = CStr(CLng(res) + CLng("0" & Mid$(ad(i), k, 1))) Next total = Right$(res, 1) & total If Len(res) - 1 < 1 Then resto = "0" Else resto = Left$(res, Len(res) - 1) End If Next If resto <> "0" Then total = resto & total End If ' sumarFilas = total End Function ' Private Function restar2Filas() As String Dim k As Long Dim m As Long Dim n As Long n = UBound(ad) ' For k = 0 To n If Len(ad(k)) > m Then m = Len(ad(k)) Next ' ' restar las filas obtenidas Dim resto As String resto = "0" Dim total As String Dim res As String ' For k = 0 To n ad(k) = Right$(String$(m, " ") & ad(k), m) Next For k = m To 1 Step -1 res = CStr(CLng("0" & Mid$(ad(0), k, 1)) - (CLng("0" & Mid$(ad(1), k, 1)) + CLng(resto))) If CLng(res) < 0 Then res = CStr(CLng(res) + 10) End If total = Right$(res, 1) & total If Len(res) - 1 < 1 Then resto = "0" Else resto = Left$(res, Len(res) - 1) End If Next If resto <> "0" Then total = resto & total End If ' restar2Filas = total End Function ' Private Sub swap(ByRef n1 As String, ByRef n2 As String) Dim t As String t = n1 n1 = n2 n2 = t End Sub