Como Implementar Procedimentos de Arredondamento Personalizados

Traduções de Artigos Traduções de Artigos
Artigo: 196652 - Ver produtos para os quais este artigo se aplica.
Expandir tudo | Reduzir tudo

Nesta página

Sumário

Existem vários algoritmos de arredondamento diferentes disponíveis nos produtos da Microsoft. Os algoritmos de arredondamento variam do Arredondamento Aritmético da função Arred() do Excel até ao Arredondamento Bancário das funções CInt(), CLng() e Round() do Visual Basic for Applications. Este artigo descreve as acções das várias funções de arredondamento do Visual Basic for Applications e fornece exemplos de utilização das funções. Para além disso, o artigo inclui funções de exemplo que implementam vários algoritmos de arredondamento.

Mais Informação

Explicação do Arredondamento

O arredondamento é necessário quando pretende converter um número de maior precisão para um número de menor precisão. O caso mais comum é quando necessita de converter um número de vírgula flutuante num número inteiro.

Arredondamento por Defeito

A forma mais simples de arredondamento é a truncagem. Quaisquer dígitos após a precisão pretendida são, simplesmente, ignorados. A função Fix() do VBA é um exemplo de truncagem. Por exemplo, Fix(3,5) é 3, e Fix(-3,5) é -3.

A função Int() arredonda por defeito, para o número inteiro mais elevado inferior ao valor. Int() e Fix() agem do mesmo modo com números positivos (truncagem) mas fornecem resultados diferentes com números negativos: Int(-3,5) é -4.

A função Fix() é um exemplo de arredondamento simétrico, porque afecta a magnitude (valor absoluto) dos números positivos e negativos do mesmo modo. A função Int() é um exemplo de arredondamento assimétrico, porque afecta a magnitude dos números positivos e negativos de modo diferente.

O Excel tem funções semelhantes: Int(), Arred.Defeito() e Arred.Para.Baixo(). Int() funciona tal como Int() no Visual Basic for Applications. Arred.Defeito() trunca os valores positivos, mas não funciona com números negativos. A função Arred.Para.Baixo() funciona como a função Fix() do VBA.

O Microsoft SQL Server tem uma função Round() que pode agir como a função Fix() do VBA. O SQL Server também tem uma função Floor(), que funciona do mesmo modo que a função Int() do VBA.

Arredondamento por Excesso

O SQL Server e o Excel têm uma função chamada Ceiling() (Arred.Excesso() no Excel), que arredonda sempre os valores fraccionais por excesso (mais positivo) para o valor seguinte.

O Visual Basic for Applications não tem uma função de arredondamento por excesso correspondente. No entanto, para os números negativos, Fix() e Int() podem ser utilizadas para arredondar por excesso, de modos diferentes.

Fix() arredonda na direcção de 0 (por excesso no sentido absoluto, mas por defeito em termos de magnitude absoluta). Fix(-3,5) é -3,5.

Int() arredonda na direcção oposta ao 0 (por excesso em termos de magnitude absoluta, mas por defeito no sentido absoluto). Int(-3,5) é -4.

Arredondamento Aritmético

Quando arredondar sempre por defeito ou por excesso, o número resultante não é necessariamente o mais próximo do número original. Por exemplo, se arredondas 1,9 por defeito para 1, a diferença é muito maior do que se arredondar por excesso para 2. É fácil de ver que os números entre 1,6 e 2,4 devem ser arredondados para 2.

No entanto, como deve ser arredondado 1,5, que é equidistante de 1 e 2? Por convenção, o número equidistante é arredondado por excesso.

É possível implementar o arredondamento de números equidistantes de modo simétrico, de modo a que -0,5 seja arredondado por defeito para -1, ou de modo assimétrico, em que -0,5 é arredondado por excesso para 0.

As funções seguintes fornecem arredondamento aritmético simétrico:
A função Arred() do Excel.
A função Round() do SQL Server pode efectuar arredondamento aritmético simétrico.

A função seguinte fornece arredondamento aritmético assimétrico:
A função Round() da biblioteca Math de Java.

O Visual Basic for Applications não tem nenhuma função que efectue o arredondamento aritmético.

Arredondamento Bancário

Quando adiciona números arredondados, o arredondamento constante de 0,5 na mesma direcção proporciona uma influência que cresce proporcionalmente à quantidade de números adicionados. Um modo de minimizar esta influência é utilizar o arredondamento bancário.

O arredondamento bancário arredonda 0,5 por excesso algumas vezes e por defeito outras vezes. A convenção é arredondar para o número par mais próximo, de modo a que 1,5 e 2,5 são ambos arredondados para 2 e 3,5 e 4,5 são ambos arredondados para 4. O arredondamento bancário é simétrico.

No Visual Basic for Applications, as funções numéricas seguintes efectuam o arredondamento bancário: CByte(), CInt(), CLng(), CCur() e Round().

Não existem funções do Excel que efectuem arredondamento bancário.

Arredondamento Aleatório

Até mesmo o arredondamento bancário pode influenciar os totais. É possível efectuar um passo adicionar para remover a influência, arredondando 0,5 por excesso ou por defeito de modo verdadeiramente aleatório. Deste modo, mesmo quando os dados são deliberadamente influenciados, é possível minimizar a influência. No entanto, a utilização do arredondamento aleatório com dados distribuídos aleatoriamente poderá originar uma influência maior do que o arredondamento bancário. O arredondamento aleatório poderá originar dois totais diferentes para os mesmos dados.

Nenhum produto da Microsoft implementa qualquer forma de procedimento de arredondamento aleatório.

Arredondamento Alternativo

O arredondamento alternativo consiste em arredondar 0,5 por excesso e 0,5 por defeito em chamadas sucessivas.

Nenhum produto da Microsoft implementa qualquer forma de procedimento de arredondamento alternativo.

A Função Arred() ou Round() é Implementada Inconsistentemente

A função Round() ou Arred() não é implementada de modo consistente nos vários produtos da Microsoft por motivos históricos.

A tabela seguinte relaciona o produto com a implementação:
   Produto                             Implementação
   ---------------------------------------------------------------------------
   Visual Basic for Applications 6.0   Arredondamento Bancário
   Excel                               Arredondamento Aritmético Simétrico
   SQL Server                          Arredondamento Aritmético Simétrico
                                       ou Arredondamento Simétrico por Defeito
                                       (Correcção) dependendo dos argumento

   Biblioteca Math de Java             Arredondamento Aritmético Assimétrico
				

A função Round() do Visual Basic 6.0 e do Visual Basic for Applications 6.0 efectua o arredondamento bancário. No entanto, possui um segundo argumento opcional que especifica o número de casas decimais para o arredondamento:
   Debug.Print Round(2.45, 1) returns 2.4.
				

Dados de Exemplo

A tabela seguinte mostra alguns dados de exemplo e os efeitos dos vários métodos de arredondamento nos números e totais gerados.
   Número/Int./Fix/Ceiling/Asym. Arit./Arit. Sim./Bancário/Aleatório/Alt.
   ---------------------------------------------------------------------
   -2.6   -3   -2  -2      -3          -3           -3       -3     -3
   -2.5   -3   -2  -2      -2          -3           -2       -2     -3
   -2.4   -3   -2  -2      -2          -2           -2       -2     -2
   -1.6   -2   -1  -1      -2          -2           -2       -2     -2
   -1.5   -2   -1  -1      -1          -2           -2       -1     -1
   -1.4   -2   -1  -1      -1          -1           -1       -1     -1
   -0.6   -1    0   0      -1          -1           -1       -1     -1
   -0.5   -1    0   0       0          -1            0       -1     -1
   -0.4   -1    0   0       0           0            0        0      0
    0.4    0    0   1       0           0            0        0      0
    0.5    0    0   1       1           1            0        1      1
    0.6    0    0   1       1           1            1        1      1
    1.4    1    1   2       1           1            1        1      1
    1.5    1    1   2       2           2            2        1      1
    1.6    1    1   2       2           2            2        2      2
    2.4    2    2   3       2           2            2        2      2
    2.5    2    2   3       3           3            2        3      3
    2.6    2    2   3       3           3            3        3      3
				

Total de todos os números:
   Número/Int./Fix/Ceiling/Asym. Arit./Arit. Sim./Bancário/Aleatório/Alt.
   ---------------------------------------------------------------------
   0.0    -9   0   9       3            0           0        1      0
				

Total de todos os números negativos:
   Número/Int./Fix/Ceiling/Asym. Arit./Arit. Sim./Bancário/Aleatório/Alt.
   ---------------------------------------------------------------------
   -13.5  -18  -9  -9      -12          -15         -13      -13    -14
				

Total de todos os números positivos:
   Número/Int./Fix/Ceiling/Asym. Arit./Arit. Sim./Bancário/Aleatório/Alt.
   ---------------------------------------------------------------------
   13.5   9    9   18      15           15          13       14     14
				

A tabela mostra a diferença entre os vários métodos de arredondamento. Para números positivos e negativos distribuídos aleatoriamente, Fix(), o arredondamento aritmético simétrico, o arredondamento bancário e o arredondamento alternativo proporcionam a diferença menos dos totais reais; o arredondamento aleatório não fica muito atrás.

No entanto, se todos os números forem positivos ou negativos, o arredondamento bancário, o arredondamento alternativo e o arredondamento aleatório proporcionam a menor diferença relativamente aos totais reais.

Exemplo de Funções de Arredondamento Definidas pelo Utilizador

O código de exemplo existente na secção Listagem de Funções seguinte fornece exemplos de implementações de cada um dos tipos de arredondamento descritos.

As funções fornecidsa são:
   AsymDown      Arredonda os números assimetricamente por defeito - 
                 semelhante a Int().
                 Os números negativos ficam mais negativos.

   SymDown       Arredonda os números simetricamente por defeito - 
                 semelhante a Fix().
                 Trunca todos os números na direcção de 0.
                 Semelhante a AsymDown para números positivos.

   AsymUp        Arredonda os números assimetricamente por excesso algumas
                 fracções.
                 Semelhante a SymDown para números negativos.
                 Semelhante a Ceiling.

   SymUp         Arredonda os números simetricamente por excesso algumas 
                 fracções - ou seja, na direcção oposta a 0.
                 Semelhante a AsymUp para números positivos.
                 Semelhante a AsymDown para números negativos.

   AsymArith     Arredondamento aritmético assimétrico - arredonda 0,5 
                 sempre por excesso.
                 Semelhante à função Round de Java.

   SymArith      Arredondamento aritmético simétrico - arredonda 0,5 na 
                 direcção oposta a 0.
                 Semelhante a AsymArith para números positivos.
                 Semelhante à função Arred. do Excel.

   BRound        Arredondamento bancário.
                 Arredonda 0,5 por excesso ou por defeito para obter um 
                 número par.
                 Simétrico por definição.

   RandRound     Arredondamento aleatório.
                 Arredonda 0,5 por excesso ou por defeito de modo aleatório.

   AltRound      Arredondamento alternativo.
                 Alterna entre o arredondamento de 0,5 por excesso ou por 
                 defeito.

   ATruncDigits  Igual a AsyncTrunc mas aceita argumentos diferentes.
				

Todas estas funções aceitam dois argumentos: o número a arredondar e um factor opcional. Se o factor for omitido, as funções devolvem um número inteiro criado por um dos métodos acima descritos. Se o factor for especificado, o número é escalado pelo factor para criar efeitos de arredondamento diferentes. Por exemplo, AsymArith(2,55, 10) produz 2,6, ou seja, arredonda para 1/factor = 1/10 = 0,1.

NOTA: Um factor de 0 gera um erro durante a execução: 1/factor = 1/0.

A tabela seguinte mostra os efeitos dos vários factores:
   Expressão       Resultado  Comentário
   ----------------------------------------------------------------------
   AsymArith(2,5)     3       Arredonda por excesso para o número inteiro
                              seguinte.
   BRound(2,18, 20)   2,2     Arredonda para os 5 cêntimos mais próximos 
                              (1/20 euros).
   SymDown(25, .1)   20       Arredonda por defeito para um múltiplo par 
                              de 10.
				

A excepção da descrição acima indicada é ADownDigits, uma função de modelo que lhe permite especificar o número de casas decimais em vez de um factor.
   Expressão           Resultado Comentário
   ------------------------------------------------------------------
   ADownDigits(2,18, 1)    2.1   Arredonda por defeito para o próximo
                                 múltiplo de 10 ^ -1.
				

Listagem de Funções


   Function AsymDown(ByVal X As Double, _
            Optional ByVal Factor As Double = 1) As Double
     AsymDown = Int(X * Factor) / Factor
   End Function

   Function SymDown(ByVal X As Double, _
            Optional ByVal Factor As Double = 1) As Double
     SymDown = Fix(X * Factor) / Factor
   '  Alternately:
   '  SymDown = AsymDown(Abs(X), Factor) * Sgn(X)
   End Function

   Function AsymUp(ByVal X As Double, _
            Optional ByVal Factor As Double = 1) As Double
   Dim Temp As Double
     Temp = Int(X * Factor)
     AsymUp = (Temp + IIf(X = Temp, 0, 1)) / Factor
   End Function

   Function SymUp(ByVal X As Double, _
            Optional ByVal Factor As Double = 1) As Double
   Dim Temp As Double
     Temp = Fix(X * Factor)
     SymUp = (Temp + IIf(X = Temp, 0, Sgn(X))) / Factor
   End Function

   Function AsymArith(ByVal X As Double, _
            Optional ByVal Factor As Double = 1) As Double
     AsymArith = Int(X * Factor + 0.5) / Factor
   End Function

   Function SymArith(ByVal X As Double, _
            Optional ByVal Factor As Double = 1) As Double
     SymArith = Fix(X * Factor + 0.5 * Sgn(X)) / Factor
   '  Alternately:
   '  SymArith = Abs(AsymArith(X, Factor)) * Sgn(X)
   End Function

   Function BRound(ByVal X As Double, _
            Optional ByVal Factor As Double = 1) As Double
   '  For smaller numbers:
   '  BRound = CLng(X * Factor) / Factor
   Dim Temp As Double, FixTemp As Double
     Temp = X * Factor
     FixTemp = Fix(Temp + 0.5 * Sgn(X))
     ' Handle rounding of .5 in a special manner
     If Temp - Int(Temp) = 0.5 Then
       If FixTemp / 2 <> Int(FixTemp / 2) Then ' Is Temp odd
         ' Reduce Magnitude by 1 to make even
         FixTemp = FixTemp - Sgn(X)
       End If
     End If
     BRound = FixTemp / Factor
   End Function

   Function RandRound(ByVal X As Double, _
            Optional ByVal Factor As Double = 1) As Double
   ' Should Execute Randomize statement somewhere prior to calling.
   Dim Temp As Double, FixTemp As Double
     Temp = X * Factor
     FixTemp = Fix(Temp + 0.5 * Sgn(X))
     ' Handle rounding of .5 in a special manner.
     If Temp - Int(Temp) = 0.5 Then
       ' Reduce Magnitude by 1 in half the cases.
       FixTemp = FixTemp - Int(Rnd * 2) * Sgn(X)
     End If
     RandRound = FixTemp / Factor
   End Function

   Function AltRound(ByVal X As Double, _
            Optional ByVal Factor As Double = 1) As Double
   Static fReduce As Boolean
   Dim Temp As Double, FixTemp As Double
     Temp = X * Factor
     FixTemp = Fix(Temp + 0.5 * Sgn(X))
     ' Handle rounding of .5 in a special manner.
     If Temp - Int(Temp) = 0.5 Then
       ' Alternate between rounding .5 down (negative) and up (positive).
       If (fReduce And Sgn(X) = 1) Or (Not fReduce And Sgn(X) = -1) Then
       ' Or, replace the previous If statement with the following to
       ' alternate between rounding .5 to reduce magnitude and increase
       ' magnitude.
       ' If fReduce Then
         FixTemp = FixTemp - Sgn(X)
       End If
       fReduce = Not fReduce
     End If
     AltRound = FixTemp / Factor
   End Function

   Function ADownDigits(ByVal X As Double, _
            Optional ByVal Digits As Integer = 0) As Double
     ADownDigits = AsymDown(X, 10 ^ Digits)
   End Function
				

NOTA: À excepção da função MArred() do Excel, as funções de arredondamento incorporadas utilizam os argumentos tal como ADownDigits, em que o segundo argumento especifica o número de dígitos em vez de um factor.

As implementações de arredondamento aqui apresentadas utilizam um factor mais flexível, tal como MArred(), visto que não necessita de efectuar o arredondamento para uma potência de 10. É possível escrever funções de wrapper à semelhança de ADownDigits.

Limitações da Vírgula Flutuante

Todas as implementações de arredondamento apresentadas aqui utilizam o tipo de dados duplo, que pode representar aproximadamente 15 casas decimais.

Visto que nem todos os valores fraccionais podem ser expressos com exactidão, poderá obter valores inesperados, porque o valor apresentado não corresponde ao valor armazenado.

Por exemplo, o número 2,25 poderá estar armazenado internamento como 2,2499999...; com o arredondamento aritmético, este número seria arredondado por defeito e não por excesso, como seria de esperar. Para além disso, a possibilidade de desvio do valor binário armazenado relativamente ao valor decimal ideal aumenta proporcionalmente ao número de cálculos efectuados sobre esse número.

Se for este o caso, poderá pretender seleccionar outro tipo de dados, tal como Moeda, que é exacto até 4 casas decimais.

Poderá igualmente tomar em consideração criar tipos de dados Variante e utilizar CDec() para converter tudo para o tipo de dados Decimal, que pode ser exacto até 28 casas decimais.

Arredondamento de Valores Monetários

Quando utiliza o tipo de dados Moeda, que é exacto até 4 casas decimais, pretende normalmente efectuar o arredondamento para 2 casas decimais (cêntimos).

A função Round2CB existente abaixo é uma variação pré-programada que efectua o arredondamento bancário para 2 casas decimais, mas não multiplica o número original. Isto evita uma possível condição de ultrapassagem de capacidade, caso o montante monetário se aproxime dos limites do tipo de dados Moeda.
   Function Round2CB (ByVal X As Currency) As Currency
     Round2CB = CCur(X / 100) * 100
   End Function
				

Arredondamento de Valores Decimais

Em seguida, apresentamos um exemplo de arredondamento aritmético assimétrico utilizando o tipo de dados Decimal:
   Function AsymArithDec(ByVal X As Variant, _
            Optional ByVal Factor As Variant = 1) As Variant
     If Not IsNumeric(X) Then
       AsymArithDec = X
     Else
       If Not IsNumeric(Factor) Then Factor = 1
       AsymArithDec = Int(CDec(X * Factor) + .5)
     End If
   End Function
				

Remover a Precisão como Atalho no Arredondamento

Conforme ensinado na escola, o arredondamento é normalmente aritmético e utiliza números positivos. Com este tipo de arredondamento, só necessita de conhecer o número 1 dígito após o destino do arredondamento. Os dígitos após a primeira casa decimal são ignorados. Por outras palavras, a precisão é removida como atalho para o arredondamento do valor.

Por exemplo, 2,5 e 2,51 são ambos arredondados por excesso para 3, enquanto que 2,4 e 2,49 são ambos arredondados por defeito para 2.

Quando utilizar o arredondamento bancário (ou qualquer outro método que arredonda 0,5 por excesso ou por defeito) ou quando arredondar números negativos utilizando o arredondamento aritmético assimétrico, a remoção da precisão poderá originar regulstados incorrectos, nos quais poderá não ser possível arredondar para o número mais próximo.

Por exemplo, com o arredondamento bancário, 2,5 é arredondado por defeito para 2 e 2,51 é arredondado por excesso para 3.

Com o arredondamento aritmético assimétrico, -2,5 é arredondado por excesso para -2 e -2,51 é arredondado por defeito para -3.

As funções definidas pelo utilizados existentes neste artigo utilizam a precisão total do número quando efectuam o arredondamento.

Referências

Ajuda do Visual Basic, versão 6.0; tópico: Int, Fix Functions; Round Function

Ajuda do Microsoft Transact SQL; tópico: Round Function; Floor Function; Ceiling Function

(c) Microsoft Corporation 1998, Todos os Direitos Reservados. Contribuições de Malcolm Stewart, Microsoft Corporation.

Propriedades

Artigo: 196652 - Última revisão: 28 de dezembro de 2007 - Revisão: 3.2
A informação contida neste artigo aplica-se a:
  • Microsoft Visual Basic Control Creation Edition
  • Microsoft Visual Basic 5.0 Learning Edition
  • Microsoft Visual Basic 6.0 Learning Edition
  • Microsoft Visual Basic 5.0 Professional Edition
  • Microsoft Visual Basic 6.0 Professional Edition
  • Microsoft Visual Basic 5.0 Enterprise Edition
  • Microsoft Visual Basic 6.0 Enterprise Edition
  • Microsoft Visual Basic for Applications 5.0
  • Microsoft Visual Basic for Applications 6.0
  • Microsoft SQL Server 6.0 Standard Edition
  • Microsoft SQL Server 6.5 Standard Edition
  • Microsoft SQL Server 7.0 Standard Edition
Palavras-chave: 
kbhowto KB196652

Submeter comentários

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com