Algumas vezes necessitamos de realizar operações com números inteiros gigantes. É o caso de algumas rotinas de criptografia.
Alguns deste números. por exemplo, podem ser vistos em http://en.wikipedia.org/wiki/RSA_numbers.
Algumas linguagens como Ruby, Python ou Julia possuem nativamente o suporte ao BigInt (inteiros Longos). Mas como Harbour vem do Clipper e no Clipper o "dinheiro da galera" não chega a cifras desta magnitude a linguagem não dá suporte nativo a tais operações. A bem da verdade nem o C faz isto nativamente.
Há algum tempo atrás escrevi algumas funções de soma, subtração, multiplicação e divisão de inteiros longos. As operações de soma, subtração e multiplicação foram adaptadas para números fracionários também.
Suponha que você queira calcular:
Código: Selecionar todos
v1 = 37975227936943673922808872755445627854565536638199
v2 = 40094690950920881030683735292761468389214899724061
? v1
? v2
? v1*v237975227936943670000000000000000000000000000000000
40094690950920880000000000000000000000000000000000
********************
Evidentemente esta não é a resposta esperada. Mas utilizando esta biblioteca você terá:
1522605027922533360535618378132637429718068114961380688657908494580122963258952897654000350692006139
Que é o resultado da multiplicação.
Segue o código fonte e alguns exemplos de operação:
Código: Selecionar todos
function main()
local n1, n2
cls
v1 = 37975227936943673922808872755445627854565536638199
v2 = 40094690950920881030683735292761468389214899724061
? v1
? v2
? v1*v2
v3 = multiplicacao("37975227936943673922808872755445627854565536638199","40094690950920881030683735292761468389214899724061")
? v3
if v3 == "1522605027922533360535618378132637429718068114961380688657908494580122963258952897654000350692006139"
? "OK"
endif
v4 = divisao(v3,"37975227936943673922808872755445627854565536638199")
if v4[1] == "40094690950920881030683735292761468389214899724061" .and. v4[2] = "0"
? "OK"
endif
n1 = " 1.2343241324312"
n2 = "9999.23412342"
? soma(n1,n2)
n1 = " 100"
n2 = "9999"
? soma(n1,n2)
n1 = "1.23"
n2 = "2.345"
? multiplicacao(n1,n2)
n1 = " -100"
n2 = " 234.4"
? multiplicacao(n1,n2)
n1 = " 100"
n2 = " 2.34257"
? subtracao(n1,n2)
n2 = " 100"
n1 = " 2.34257"
? subtracao(n1,n2)
n2 = "-100"
n1 = "-2.34257"
? soma(n1,n2)
n1 = "-5"
n2 = "10"
? soma(n1,n2)
n2 = "-5"
n1 = "10"
? soma(n1,n2)
n1 = "60"
n2 = "20"
vet= divisao(n1,n2)
? vet[1]
? vet[2]
n1 = "20"
n2 = "60"
vet= divisao(n1,n2)
? vet[1]
? vet[2]
n1 = "20"
n2 = "-60"
vet= divisao(n1,n2)
? vet[1]
? vet[2]
n1 = "-20"
n2 = "60"
vet= divisao(n1,n2)
? vet[1]
? vet[2]
n1 = "-60"
n2 = "20"
vet= divisao(n1,n2)
? vet[1]
? vet[2]
n1 = "-63"
n2 = "20"
vet= divisao(n1,n2)
? vet[1]
? vet[2]
n1 = "114381625757888867669235779976146612010218296721242362562561842935706935245733897830597123563958705058989075147599290026879543541"
n2 = "3490529510847650949147849619903898133417764638493387843990820577"
vet = divisao(n1,n2)
? vet[1]
? vet[2]
return nil
/**
-----------------------------------------------------------------------------------------------------------------------
Função para efetuar a soma de números inteiros positivos grandes em base 10
Parâmetros:
1. n1 = primeiro número
2. n2 = segundo número
Retorno: o resultado da soma entre os dois números
-----------------------------------------------------------------------------------------------------------------------
*/
function soma(n1,n2)
local tam1:=0, tam2:=0, tmp:=0, aux:=0, ct:=0, soma:=0, resultado:="", fracao:=.f., vet_int:=array(3), sinal := ""
n1 := alltrim(n1)
n2 := alltrim(n2)
if (substr(n1,1,1)=="-" .and. substr(n2,1,1)=="-" ) // soma de dois números negativos
n1 = substr(n1,2)
n2 = substr(n2,2)
sinal := "-"
elseif (substr(n1,1,1)<>"-" .and. substr(n2,1,1)=="-" ) // subtracao normal
resultado = subtracao(n1,substr(n2,2))
return resultado
elseif (substr(n1,1,1)=="-" .and. substr(n2,1,1)<>"-" ) // subtracao normal (negativo - positivo)
resultado = subtracao(n2,substr(n1,2))
return resultado
else
sinal := ""
endif
// rotina para permitir operação com números fracionários
if (at(".",n1)==0 .and. at(".",n2)==0)
fracao := .f.
else
fracao := .t.
vet_int = transforma_para_inteiro(n1,n2)
n1 = vet_int[1]
n2 = vet_int[2]
endif
tam1 := len(n1)
tam2 := len(n2)
tmp := max(tam1,tam2)
// tornando o tamanho dos numeros iguais
n1 = replicate("0",tmp-tam1) + n1
n2 = replicate("0",tmp-tam2) + n2
for ct:= tmp to 1 step -1
soma += val(substr(n1,ct,1)) + val(substr(n2,ct,1))
aux:=soma % 10
resultado = str(int(aux),1) + resultado
soma = (soma - aux)/10
next
if soma <> 0
resultado = alltrim(str(int(soma))) + resultado
endif
if fracao == .t.
resultado = transforma_para_fracionario(resultado,vet_int[3])
endif
resultado = sinal + resultado
return resultado
/**
-----------------------------------------------------------------------------------------------------------------------
Função para efetuar a soma de multiplicacao inteiros positivos grandes em base 10
Parâmetros:
1. n1 = primeiro número
2. n2 = segundo número
Retorno: A função retorna o resultado da multiplicação
-----------------------------------------------------------------------------------------------------------------------
*/
function multiplicacao(n1,n2)
local tam2:=0, tmp:=0, aux:=0, ct:=0, resultado:="0", mat:=array(9), fator:=.f.,vet_inc:=array(3), fracao2:=.f., sinal:=""
n1 := alltrim(n1)
n2 := alltrim(n2)
if (substr(n1,1,1)<>"-" .and. substr(n2,1,1)<>"-")
sinal := ""
elseif (substr(n1,1,1)=="-" .and. substr(n2,1,1)<>"-")
sinal := "-"
elseif (substr(n1,1,1)<>"-" .and. substr(n2,1,1)=="-")
sinal := "-"
else
sinal := ""
endif
n1 = strtran(n1,"-","")
n2 = strtran(n2,"-","")
// rotina para permitir operação com números fracionários
if (at(".",n1)==0 .and. at(".",n2)==0)
fracao2 := .f.
else
fracao2 := .t.
vet_int = transforma_para_inteiro(n1,n2)
n1 = vet_int[1]
n2 = vet_int[2]
? n1
? n2
endif
do while len(n1) > 1
if substr(n1,1,1)=="0"
n1 = substr(n1,2)
else
exit
endif
enddo
do while len(n2) > 1
if substr(n2,1,1)=="0"
n2 = substr(n2,2)
else
exit
endif
enddo
if n1 == "0" .or. n2 == "0"
return "0"
endif
tam2 := len(n2)
// aqui temos as operações de multiplicação do número 2 de 1 até 9 para facilitar as operações posteriores
mat[1] := n1
tmp := n1
for ct:= 2 to 9
tmp := soma(tmp,n1)
mat[ct] := tmp
next
// Multiplicação propriamente dita
tmp := ""
for ct:= tam2 to 1 step -1
aux := val(substr(n2,ct,1))
if aux <> 0
resultado = soma(resultado,(mat[aux]+tmp))
endif
tmp+="0"
next
if fracao2 == .t.
resultado = transforma_para_fracionario(resultado,vet_int[3]*2)
endif
resultado = sinal + resultado
return resultado
/**
-----------------------------------------------------------------------------------------------------------------------
Função para efetuar a comparação de dois números inteiros positivos grandes em base 10
Parâmetros:
1. n1 = primeiro número
2. n2 = segundo número
Retorno:
A função retorna [0] se os números são iguais
[1] se o primeiro número é o maior
[2] se o segundo número é o maior
-----------------------------------------------------------------------------------------------------------------------
*/
function comparar(n1,n2)
local tam1:=0, tam2:=0, ct:=0, ret := 0
n1 := alltrim(n1)
n2 := alltrim(n2)
do while len(n1) > 1
if substr(n1,1,1)=="0"
n1 = substr(n1,2)
else
exit
endif
enddo
do while len(n2) > 1
if substr(n2,1,1)=="0"
n2 = substr(n2,2)
else
exit
endif
enddo
tam1 := len(n1)
tam2 := len(n2)
do case
case tam1 > tam2
return 1
case tam2 > tam1
return 2
endcase
if n1 == n2 // verifica se os números são iguais
return 0
endif
// se os números tem o mesmo tamanho então temos que verificar dígito a digito
for ct:= 1 to tam1
if val(substr(n1,ct,1)) > val(substr(n2,ct,1))
ret := 1
exit
elseif val(substr(n2,ct,1)) > val(substr(n1,ct,1))
ret:= 2
exit
endif
next
return ret
/**
-----------------------------------------------------------------------------------------------------------------------
Função para efetuar a subtração de números inteiros positivos grandes em base 10
Parâmetros:
1. n1 = primeiro número
2. n2 = segundo número
Retorno:
A função retorna o resultado da subtração (Número maior - Número Menor)
-----------------------------------------------------------------------------------------------------------------------
*/
function subtracao(n1,n2)
local tam1:=0, tam2:=0, tmp:=0, aux:=0, ct:=0, aux2:=0, subtracao:=0, resultado:="", aux3:="", fracao:=.f., vet_inc:=array(3), inverte:=.f.
n1 := alltrim(n1)
n2 := alltrim(n2)
if (substr(n1,1,1)=="-" .or. substr(n2,1,1)=="-" )
? "Não é possível definir uma operação de subtração com números com sinal!!!"
quit
endif
// rotina para permitir operação com números fracionários
if (at(".",n1)==0 .and. at(".",n2)==0)
fracao := .f.
else
fracao := .t.
vet_int = transforma_para_inteiro(n1,n2)
n1 = vet_int[1]
n2 = vet_int[2]
endif
tmp := comparar(n1,n2) // comparando os números
if tmp == 0
return "0" // números iguais
elseif tmp == 2 // inverte os números para facilitar a operação
inverte := .t.
aux3 = n1
n1 = n2
n2 = aux3
endif
// Iniciando a operação para a subtracao
do while len(n1) > 1
if substr(n1,1,1)=="0"
n1 = substr(n1,2)
else
exit
endif
enddo
do while len(n2) > 1
if substr(n2,1,1)=="0"
n2 = substr(n2,2)
else
exit
endif
enddo
tam1 := len(n1)
tam2 := len(n2)
tmp := max(tam1,tam2)
// tornando o tamanho dos numeros iguais
n1 = replicate("0",tmp-tam1) + n1
n2 = replicate("0",tmp-tam2) + n2
for ct:= tmp to 1 step -1
subtracao = val(substr(n1,ct,1)) - (val(substr(n2,ct,1)) + aux2)
aux2 := 0
if subtracao < 0
subtracao += 10
aux2 ++
endif
resultado = str(int(subtracao),1) + resultado
next
do while.t.
if substr(resultado,1,1)=="0"
resultado = substr(resultado,2)
else
exit
endif
enddo
if fracao == .t.
resultado = transforma_para_fracionario(resultado,vet_int[3])
endif
if inverte == .t.
resultado = "-" + resultado
endif
return resultado
/**
-----------------------------------------------------------------------------------------------------------------------
Função para efetuar a divisao de números inteiros positivos grandes em base 10
Parâmetros:
1. n1 = primeiro número
2. n2 = segundo número
Retorno:
A função retorna o resultado da divisao com um vetor {a,b} onde [a] é o resultado da divisao inteira e [b] = resto
Assim só é possível dividir um número A por um número B quando A for maior que B
-----------------------------------------------------------------------------------------------------------------------
*/
function divisao(n1,n2)
local tam1:=0, tam2:=0, tmp:=0, aux:=0, aux2:="", divisao:=0, resultado:={"",""}, aux3:="", operador:="", ct:= 0, ret:=0, sinal:=""
n1 := alltrim(n1)
n2 := alltrim(n2)
if (substr(n1,1,1)<>"-" .and. substr(n2,1,1)<>"-")
sinal := ""
elseif (substr(n1,1,1)=="-" .and. substr(n2,1,1)<>"-")
sinal := "-"
elseif (substr(n1,1,1)<>"-" .and. substr(n2,1,1)=="-")
sinal := "-"
else
sinal := ""
endif
n1 = strtran(n1,"-","")
n2 = strtran(n2,"-","")
do while len(n1) > 1
if substr(n1,1,1)=="0"
n1 = substr(n1,2)
else
exit
endif
enddo
do while len(n1) > 2
if substr(n2,1,1)=="0"
n2 = substr(n2,2)
else
exit
endif
enddo
if (n2=="1")
resultado[1] := n1
resultado[2] := "0"
return resultado
endif
tmp := comparar(n1,n2) // comparando os números
if tmp == 0
return {sinal+"1","0"} // números iguais - divisao == 1, resto 0
elseif tmp == 2 // verifica se n2 é maior que n1 porque tratamos de inteiros positivos
return {"0",sinal + n1}
endif
tam1 := len(n1)
tam2 := len(n2)
tmp := max(tam1,tam2)
aux := len(n2) // numero de digitos do divisor
operador := n1 + "#"
aux2 := substr(operador,1,aux)
if comparar(aux2,n2) <> 2
operador := substr(operador,aux+1)
else
aux++
aux2 := substr(operador,1,aux)
operador := substr(operador,aux+1)
endif
do while.t.
// verificado o resultado da divisao
ret := 1
do while.t.
aux3 := multiplicacao(n2,str(int(ret),1))
divisao := comparar(aux2,aux3)
if divisao == 2
ret:=ret-1
aux3 := multiplicacao(n2,str(int(ret),1))
exit
elseif divisao == 0
exit
endif
if ret == 9
exit
endif
++ret
enddo
resultado[1] += str(int(ret),1)
aux2 = subtracao(aux2,aux3) // controla o resto
if aux2 == "0" .and. substr(operador,1,1)=="#" // divisao exata
exit
endif
do while.t.
if substr(operador,1,1)=="#"
exit
endif
aux2 := aux2 + substr(operador,1,1)
do while.t.
if len(aux2) == 1
exit
endif
if substr(aux2,1,1)=="0"
aux2 = substr(aux2,2)
else
exit
endif
enddo
operador := substr(operador,2)
divisao = comparar(aux2,n2)
if divisao <> 2
exit
else
resultado[1] += "0"
endif
enddo
if substr(operador,1,1)=="#"
exit
endif
enddo
// verificando o resto final
aux := 0
if aux2 <> "0"
do while.t.
if comparar(aux2,n2) < 2
++aux
aux2 = subtracao(aux2,n2)
else
exit
endif
enddo
endif
if aux <> 0
resultado[1] = resultado[1] + str(int(aux),1)
endif
aux := 0
do while.t.
if len(aux2) == 1
exit
endif
if substr(aux2,1,1)=="0"
++aux
aux2 = substr(aux2,2)
else
exit
endif
enddo
resultado[1] += replicate("0",aux)
resultado[1] = sinal + resultado[1]
resultado[2] = sinal + aux2 // resto da divisao inteira
return resultado
/**
-----------------------------------------------------------------------------------------------------------------------
Função para transformar números fracionários em inteiros proporcionalmente
Parâmetros:
1. n1 = primeiro número
2. n2 = segundo número
Retorno:
A função retorna um vetor com 3 valores
v[1] = primeiro número convertido para inteiros
v[2] = segundo número convertido para inteiros
v[3] = Número de zeros acrescentados para converter os dois números para inteiros proporcionalmente
OBS: separador decimal é o ponto!!!
-----------------------------------------------------------------------------------------------------------------------
*/
function transforma_para_inteiro(n1,n2)
local vet:=array(3), dec1:=0, dec2:= 0, tam1:=0, tam2:=0, fator:=0, posicao := 0, guarda:=""
n1 := alltrim(n1)
n2 := alltrim(n2)
tam1 = len(n1)
tam2 = len(n2)
dec1 = at(".",n1)
dec2 = at(".",n2)
if dec1 == 0 .and. dec2 == 0 // números inteiros
vet[1] = n1
vet[2] = n2
vet[3] = 1
return vet
endif
if dec1 <> 0
dec1 = tam1 - dec1
endif
if dec2 <> 0
dec2 = tam2 - dec2
endif
fator:= max(dec1,dec2)
posicao = fator
do while posicao > 0
if (at(".",n1) <> len(n1) .and. at(".",n1) <> 0 )
guarda = substr(n1, at(".",n1) + 1, 1)
n1 = stuff(n1, at(".",n1) + 1, 1, ".")
n1 = stuff(n1, at(".",n1), 1, guarda)
else
n1 := strtran(n1,".","")
n1 = n1 + "0"
endif
if (at(".",n2) <> len(n2) .and. at(".",n2) <> 0 )
guarda = substr(n2, at(".",n2) + 1, 1)
n2 = stuff(n2, at(".",n2) + 1, 1, ".")
n2 = stuff(n2, at(".",n2), 1, guarda)
else
n2 := strtran(n2,".","")
n2 = n2 + "0"
endif
--posicao
enddo
n1 = strtran(n1,".","")
n2 = strtran(n2,".","")
vet[1] = alltrim(n1)
vet[2] = alltrim(n2)
vet[3] = fator
return vet
/**
-----------------------------------------------------------------------------------------------------------------------
Função para transformar números inteiros em decimais-fracionários proporcionalmente ao fator
Parâmetros:
1. n1 = primeiro número inteiro
2. fator = Número de zeros necessários para transformação
Retorno:
A função retorna o número no formato decimal-fracionário
OBS: separador decimal é o ponto!!!
-----------------------------------------------------------------------------------------------------------------------
*/
function transforma_para_fracionario(n1,fator)
local ct:=0, guarda:="", posicao:=0
n1 := alltrim(n1)
if fator > (len(n1)-2)
n1 = replicate("0",fator-len(n1) + 4) + n1
else
n1 = replicate("0",2) + n1
endif
for ct:= 1 to fator
if (at(".",n1)==0)
n1 = substr(n1,1,len(n1)-1) + "." + substr(n1,len(n1),1)
else
posicao = at(".",n1)
guarda = substr(n1, posicao - 1, 1)
n1 = stuff(n1, posicao - 1, 1, ".")
n1 = stuff(n1, posicao, 1, guarda)
endif
next
do while.t.
if (substr(n1,1,1)=="0")
n1 = substr(n1,2)
else
exit
endif
enddo
n1 = alltrim(n1)
if (substr(n1,1,1)==".")
n1 = "0" + n1
endif
do while.t.
if (substr(n1,len(n1),1)=="0")
n1 = substr(n1,1,len(n1)-1)
else
exit
endif
enddo
if (substr(n1,len(n1),1)==".")
n1 = substr(n1,1,len(n1)-1)
endif
return alltrim(n1)
// END *********************************************************************************************************************************
Abraço,
Yugi

