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.
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.
"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():
Função RetPictVal():
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 )
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
...
E ae Naldo DJ, tudo certo?
ResponderExcluirSe 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
Anônimo (rs)
ResponderExcluirVou 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
...