BlackTDN Search

segunda-feira, 30 de agosto de 2010

Protheus :: Multiplicando Números Absurdamente Grandes ( Método da Gelosia )

É sabido que no Protheus existe uma limitação, imposta pelo pessoal de tecnologia, no que diz respeito ao tamanho dos números e nas operações que se podem realizar sobre eles. Essa limitação fez com que criassem a função MathC que realiza operações matemáticas usando "strings". Dependendo do tamanho do número, o retorno de MathC talvez não seja o que você espera.

Nesse caso, poderá recorrer à função MultStr(cNum1,cNum2), criada pelo "Automan" vulgo, Júlio Louco. Ela também faz a multiplicação usando "strings" numéricas, e, acredito eu, que ele tenha usado o Método da Gelosia para esse fim.

Mas afinal, o que é o Método da Gelosia?

Um Pouco de História:

"Os matemáticos hindus desenvolveram um método de multiplicação através de tábuas quadriculadas. Mais tarde os árabes o levaram para a Europa e ficou conhecido como Método da Gelosia" (fonte: Blog da Professora Ju). Para maiores informações sobre o "Método da Gelosia" consulte o Blog da Professora JU ou outras referências como o "wiki" sobre "Leonardo de Pisa" mais conhecido como "Fibonacci". Encontrei um "post " legal sobre esse assunto em: "Inveja Matemática". Recomendo a leitura desse último.

Vamos agora criar a nossa própria função GMultiply( cN1 , cN2 )  que irá retornar, também, a multiplicação de valores numéricos muito, mas muito, mas muito grandes, passados como "string" para a função. O G, prefixando a função, é de Gelosia. Comparei os valores de retorno com a da MultStr e, que coincidência, são exatamente iguais, com uma diferença, a nossa será muito mais rápida.

Ao que interessa. O código:

Função GMultiply:


1: /*/
 2:  Funcao:  GMultiply
 3:  Autor:  Marinaldo de Jesus
 4:  Data:  29/08/2010
 5:  Descricao: Multiplica um Numero utilizando o metodo Gelosia
 6:  Sintaxe: <Vide Parametros Formais>
 7: /*/
 8: Static Function GMultiply( cN1 , cN2 )
 9: 
10:  Local a
11:  Local b
12:  Local c
13: 
14:  Local cRet := ""
15: 
16:  Local g  := 0
17:  Local h  := 0
18:  Local i  := 0
19:  Local j  := 0
20:  Local k  := 0
21:  Local l  := 0
22:  Local m  := 0
23:  Local n  := 0
24:  Local x  := 0
25:  Local y  := 0
26: 
27:  h := Len( cN1 )
28:  g := Len( cN2 )
29: 
30:  y := Max( h , g )
31:  a := StrByteToArr( Inverte( PadL( cN1 , y , "0" ) ) , .T. )
32:  b := StrByteToArr( Inverte( PadL( cN2 , y , "0" ) ) , .T. )
33: 
34:  y := ( h + g )
35: 
36:  c := Array( y )
37: 
38:  aFill( c , 0 )
39: 
40:  n := Max( h , g )
41:  k := 1
42: 
43:  i := 1
44:  While ( i <= n )
45:   s := 1
46:   j := i
47:   While ( s <= i )
48:    c[k] += ( a[s] * b[j] )
49:    s++
50:    j--
51:   End While
52:   IF ( c[k] >= 10 )
53:    c[k+1] := Int( c[k] / 10 )
54:    c[k] -= ( Int( c[k] / 10 ) * 10 )
55:   EndIF
56:   k++
57:   i++
58:  End While
59: 
60:  l := 2
61:  While ( l <= n )
62:   s := n
63:   j := l
64:   While ( s >= l )
65:    c[k] += ( a[s] * b[j] )
66:    s--
67:    j++
68:   End While
69:   IF ( c[k] >= 10 )
70:    c[k+1] := Int( c[k] / 10 )
71:    c[k] -= ( Int( c[k] / 10 ) * 10 )
72:   EndIF
73:   k++
74:   IF ( k >= y )
75:    Exit
76:   EndIF
77:   l++
78:  End While
79: 
80:  x := k
81:  While ( x >= 1 )
82:   While ( ( x >= 1 ) .and. ( c[x] == 0 ) )
83:    x--
84:   End While
85:   While ( x >= 1 )
86:    cRet += Str( c[x] , 1 )
87:    x--
88:   End While
89:  End While
90: 
91: Return( cRet )
      

Essa função fara uso de mais três funções complementares, sendo elas, e diretamente: StrByteToArray() e Inverte(); e a RetPictVal(), indiretamente (usada na Inverte()). Abaixo o código de cada uma delas:

Função StrByteToArray():

1: /*/
 2:  Funcao:  StrByteToArr
 3:  Autor:  Marinaldo de Jesus
 4:  Data:  08/11/2005
 5:  Descricao: Retornar Array com a os Bytes de uma String de Caractere
 6:  Sintaxe: <Vide Parametros Formais>
 7: /*/
 8: Static Function StrByteToArr( cStr , lToVal )
 9: 
10:  Local aStrByteArr := {}
11:  
12:  Local cByte
13:  
14:  Local nByte
15:  Local nBytes
16: 
17:  DEFAULt lToVal := .F.
18: 
19:  nByte  := 0
20:  nBytes := Len( cStr )
21:  While ( ( ++nByte ) <= nBytes )
22:   cByte := SubStr( cStr , nByte , 1 )
23:   aAdd( aStrByteArr , IF( lToVal , Val( cByte ) , cByte ) )
24:  End While
25: 
26: Return( aStrByteArr )
      

Função Inverte():

1: /*/
 2:  Funcao:  Inverte
 3:  Autor:  Marinaldo de Jesus
 4:  Data:  08/11/2005
 5:  Descricao: Inverte o conteudo de uma string
 6:  Sintaxe: <Vide Parametros Formais>
 7: /*/
 8: Static Function Inverte( uInverte )
 9: 
10:  Local cValType  := ValType( uInverte )
11:  Local cInverte  := ""
12:  Local cRetorno   := ""
13: 
14:  IF ( cValType == "D" )
15:   cInverte   := Dtos( uInverte )
16:  ElseIF ( cValType $ "A/O" ) 
17:   cInverte  := ""
18:  ElseIF ( cValType == "N" )
19:   cInverte   := Transform( uInverte , RetPictVal( uInverte ) )
20:  ElseIF ( cValType == "L" )
21:   IF ( uInverte )
22:    cInverte := "0"
23:   Else
24:    cInverte := "1"   
25:   EndIF
26:  ElseIF ( cValType == "C" )
27:   cInverte   := uInverte
28:  Else
29:   cInverte     := ""
30:  EndIF 
31: 
32:  nIndex := Len( cInverte )
33: 
34:  While ( nIndex > 0 )
35:   cRetorno += SubStr( cInverte , nIndex , 1 )
36:   --nIndex
37:  End While
38: 
39: Return( cRetorno )
      
Função RetPictVal():

1: /*/
 2:  Funcao:  RetPictVal
 3:  Autor:  Marinaldo de Jesus 
 4:  Data:  08/11/2005
 5:  Descricao: Retorna a Picture para Campo Numerico Conforme Valor
 6:  Sintaxe: <Vide Parametros Formais>
 7: /*/
 8: Static Function RetPictVal( nVal , lDecZero , nInt , nDec , lPictSepMil )
 9: 
10:  Local cPict
11:  Local cPictSepMil
12:  
13:  Local uInt
14:  Local uDec
15:  
16:  IF ( ValType( nVal ) == "N" )
17:   uInt := Int( nVal )
18:   uDec := ( nVal - uInt )
19:   DEFAULT lDecZero := .F.
20:   IF (;
21:     ( uDec == 0 );
22:     .and.;
23:     !( lDecZero );
24:    )
25:    uDec := NIL
26:   EndIF
27:   IF ( uDec <> NIL )
28:    uDec := AllTrim( Str( uDec ) )
29:    uDec := SubStr( uDec , At( "." , uDec ) + 1 )
30:    uDec := Len( uDec )
31:   EndIF
32:   uInt := Len( AllTrim( Str( uInt ) ) )
33:   nInt := uInt
34:   cPict := Replicate( "9" , uInt )
35:   DEFAULT lPictSepMil := .F.
36:   IF ( lPictSepMil )
37:    IF ( nInt > 3 )
38:     cPictSepMil := cPict
39:     cPict  := ""
40:     For uInt := nInt To 1 Step - 3
41:      cPict := ( "," + SubStr( cPictSepMil , -3 , uInt ) + cPict )
42:     Next uInt
43:    EndIF
44:   EndIF
45:   IF ( uDec <> NIL )
46:    cPict += "."
47:    cPict += Replicate( "9" , uDec )
48:    nDec := uDec
49:   EndIF
50:  EndIF
51: 
52: Return( cPict )
      


Exemplos de uso da GMultiply(),da MultStr() e da MathC(): Obs.: vou usar valores bem grandes (uma vez que valores "pequenos" são tratados automaticamente pelos operadores da linguagem).


Para:
cN1 := "11111111"
cN2 := "11111111"

Operando com (*)

Val(cN1)*Val(cN2)
Resultado: 123456787654321

Operando com MathC
MathC(cN1,"*",cN2)

Resultado: 1.234567876543210000e+014

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 123456787654321

Operando com GMultiply
GMultiply(cN1,cN2)
Resultado: 123456787654321

Para:

cN1 := "111111111"
cN2 := "111111111"

Operando com (*)

Val(cN1)*Val(cN2)
Resultado: 12345678987654320

Operando com MathC
MathC(cN1,"*",cN2)

Resultado: 1.234567898765432000e+016

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 12345678987654321

Operando com GMultiply
GMultiply(cN1,cN2)
Resultado: 12345678987654321

Para:

cN1 := "1111111111"
cN2 := "1111111111"

Operando com (*)

Val(cN1)*Val(cN2)
Resultado: 1234567900987654400

Operando com MathC
MathC(cN1,"*",cN2)

Resultado: 1.234567900987654400e+018

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 1234567900987654321

Operando com GMultiply
GMultiply(cN1,cN2)
Resultado: 1234567900987654321

Para:

cN1 := "11111111111"
cN2 := "11111111111"

Operando com (*)

Val(cN1)*Val(cN2)
Resultado: 123456790120987660000

Operando com MathC
MathC(cN1,"*",cN2)

Resultado: 1.234567901209876600e+020

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 123456790120987654321

Operando com GMultiply
GMultiply(cN1,cN2)
Resultado: 123456790120987654321

Para:

cN1 := "111111111111"
cN2 := "111111111111"

Operando com (*)

Val(cN1)*Val(cN2)
Resultado: 12345679012320988000000

Operando com MathC
MathC(cN1,"*",cN2)

Resultado: 1.234567901232098800e+022

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 12345679012320987654321

Operando com GMultiply
GMultiply(cN1,cN2)
Resultado: 12345679012320987654321

Para:

cN1 := "1111111111111"
cN2 := "1111111111111"

Operando com (*)

Val(cN1)*Val(cN2)
Resultado: 1234567901234320900000000

Operando com MathC
MathC(cN1,"*",cN2)

Resultado: 1.234567901234320900e+024

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 1234567901234320987654321

Operando com GMultiply
GMultiply(cN1,cN2)
Resultado: 1234567901234320987654321

Para:

cN1 := "11111111111111"
cN2 := "11111111111111"

Operando com (*)

Val(cN1)*Val(cN2)
Resultado: 123456790123454320000000000

Operando com MathC
MathC(cN1,"*",cN2)

Resultado: 1.234567901234543200e+026

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 123456790123454320987654321

Operando com GMultiply
GMultiply(cN1,cN2)
Resultado: 123456790123454320987654321

Para:

cN1 := "111111111111111"
cN2 := "111111111111111"

Operando com (*)

Val(cN1)*Val(cN2)
Resultado: 12345679012345654000000000000

Operando com MathC
MathC(cN1,"*",cN2)

Resultado: 1.234567901234565400e+028

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 12345679012345654320987654321

Operando com GMultiply
GMultiply(cN1,cN2)
Resultado: 12345679012345654320987654321

Para:

cN1 := "1111111111111111"
cN2 := "1111111111111111"

Operando com (*)

Val(cN1)*Val(cN2)
Resultado: ******************************

Operando com MathC
MathC(cN1,"*",cN2)
Resultado: 1.234567901234567700e+030

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 1234567901234567654320987654321

Operando com GMultiply
GMultiply(cN1,cN2)

Resultado: 1234567901234567654320987654321

Para:
cN1 := "111111111111111111111111"
cN2 := "1111111111111111111111111"

Operando com (*)
Val(cN1)*Val(cN2)
Resultado: ******************************

Operando com MathC
MathC(cN1,"*",cN2)
Resultado: 1.234567901234567700e+039

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 1234567901234567777777777654320987654321

Operando com GMultiply
GMultiply(cN1,cN2)

Resultado: 1234567901234567777777777654320987654321

Para:
cN1 := "11111111111111111111111111111111111111111111111111111"
cN2 := "11111111111111111111111111111111111111111111111111111"

Operando com (*)
Val(cN1)*Val(cN2)
Resultado: ******************************

Operando com MathC
MathC(cN1,"*",cN2)
Resultado: 1.234567901234567800e+104

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 123456790123456790123456790123456790123456790123456787654320987654320987654320987654320987654320987654321

Operando com GMultiply
GMultiply(cN1,cN2)

Resultado: 123456790123456790123456790123456790123456790123456787654320987654320987654320987654320987654320987654321

Para:
cN1 := "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
cN2 := "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111"

Operando com (*)
Val(cN1)*Val(cN2)
Resultado: ******************************

Operando com MathC
MathC(cN1,"*",cN2)
Resultado: 1.234567901234567800e+168

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 1234567901234567901234567901234567901234567901234567901234567901234567901234567901234320987654320987654320987654320987654320987654320987654320987654320987654320987654321

Operando com GMultiply
GMultiply(cN1,cN2)

Resultado: 1234567901234567901234567901234567901234567901234567901234567901234567901234567901234320987654320987654320987654320987654320987654320987654320987654320987654320987654321

Para:
cN1 := "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
cN2 := "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"

Operando com (*)
Val(cN1)*Val(cN2)
Resultado: ******************************

Operando com MathC
MathC(cN1,"*",cN2)
Resultado: 1.#INF00000000000000e+000

Operando com MultStr
MultStr(cN1,cN2)
Resultado: 123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456787654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654321

Operando com GMultiply
GMultiply(cN1,cN2)

Resultado: 123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456790123456787654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654321
      


Os testes foram efetuados usando-se o "Totvs Development Studio (IDE)" do Protheus. Observe que ao usar o operador de multiplicação (*), quando maior o número menor a precisão na operação.

Dica: Para valores Grandes, mas muito grandes, não use o operador de multiplicação do Protheus (*) e muito menos a função Val() para converter a string em um número, vai deixar seu servidor "Maluquinho".

Para saber se o resultado da nossa função é igual ao resultado obtido através da MultStr basta compará-los com: GMultiply(cNum1,cNum2) == MultStr(cNum1,cNum2).

Bem, é isso. Para obter o código de exemplo clique aqui.

[]s
иαldσ dj
...

2 comentários:

  1. E ae Naldo DJ, tudo certo?

    Se possível falar um pouco sobre Modelo 3, tela semelhante a tela do PEDIDO DE VENDA.

    Tempos atrás fiz uma tela deste tipo. Apanhei demais para conseguir.

    Abraços

    ResponderExcluir
  2. Anônimo (rs)

    Vou fazer melhor que isso. Além de falar sobre a Modelo 3 vou explicar-lhe como criar a sua própria Modelo3. Aguarde.

    []s
    иαldσ dj
    ...

    ResponderExcluir