Como implementar os procedimentos de arredondamento personalizado

Traduções deste artigo Traduções deste artigo
ID do artigo: 196652 - Exibir os produtos aos quais esse artigo se aplica.
Expandir tudo | Recolher tudo

Neste artigo

Sumário

Há vários algoritmos de arredondamento diferentes disponíveis nos produtos Microsoft. Os algoritmos de arredondamento variam de Arredondamento aritmético na função Worksheet Round() do Excel até Banker's Rounding nas funções CInt(), CLng() e Round() no Visual Basic for Applications. Este artigo descreve o que as diversas funções de arredondamento do Visual Basic for Applications fazem e apresenta exemplos do uso das funções. Além disso, o artigo inclui funções de exemplo que implementam vários algoritmos de arredondamento.

Mais Informações

Arredondamento explicado

É necessário arredondar quando você desejar converter um número de precisão alta para uma precisão menor. O caso mais comum é quando você precisa converter um número do ponto flutuante para um inteiro.

Arredondando para menos

A forma mais simples de arredondamento é o truncamento. Todos os dígitos depois da precisão desejada são simplesmente ignorados. A função VBA Fix() é um exemplo de truncamento. Por exemplo, Fix(3.5) é 3 e Fix(-3.5) é -3.

A função Int() arredonda para baixo para o maior número inteiro menor que o valor. Int() e Fix() funcionam da mesma maneira com números positivos - truncando - mas dão resultados diferentes para números negativos: Int(-3.5) mostra -4.

A função Fix() é um exemplo do arredondamento simétrico, pois afeta a magnitude (o valor absoluto) dos números positivos e negativos da mesma maneira. A função Int() é um exemplo do arredondamento assimétrico, pois afeta a magnitude dos números positivos e negativos de maneira diferente.

O Excel tem funções de planilha semelhantes: Int(), Floor() e RoundDown(). Int() funciona da mesma maneira que Int() no Visual Basic for Applications. Floor() trunca valores positivos, mas não funciona com números negativos. A função RoundDown() funciona da mesma maneira que a função VBA Fix().

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

Arredondando para mais

O SQL Server e o Excel têm uma função chamada Ceiling(), que sempre arredonda valores de fração para o próximo valor mais alto (mais positivo).

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

Fix() arredonda para 0 (acima no sentido absoluto, mas abaixo em termos de magnitude absoluta). Fix(-3.5) é -3.5.

Int() arredonda para mais de 0 (acima em termos de magnitude absoluta, mas abaixo no sentido absoluto). Int(-3.5) é -4.

Arredondamento aritmético

Ao arredondar sempre para mais ou para menos, o número resultante não é necessariamente o mais próximo do número original. Por exemplo, se você arredondar 1.9 para 1, a diferença é muito maior do que se você arredondar para 2. É fácil ver que números de 1.6 para 2.4 devem ser arredondados para 2.

No entanto, e se for 1.5, que é equidistante entre 1 e 2? Por convenção, o número intermediário é arredondado para mais.

É possível implementar o arredondamento de números intermediários de modo simétrico, de modo que -.5 seja arredondado para -1 ou de modo assimétrico, em que -.5 é arredondado para 0.

As seguintes funções oferecem arredondamento aritmético simétrico:
A função Round() da planilha do Excel.
A função Round() do SQL Server pode fazer o arredondamento aritmético simétrico.

A seguinte função oferece arredondamento aritmético assimétrico:
O método Round() da biblioteca Java Math.

O Visual Basic for Applications não possui nenhuma função que faça o arredondamento aritmético.

Banker's Rounding

Ao adicionar valores arredondados juntos, o arredondamento .5 sempre na mesma direção resulta em um ajuste que aumenta com os demais números que você adicionar. Uma maneira para diminuir o ajuste é o banker's rounding.

O Banker's rounding às vezes arredonda .5 para mais e, às vezes para menos. A convenção é arredondar para o número par mais próximo, de modo que 1.5 e 2.5 sejam arredondados para 2 e que, 3.5 e 4.5 sejam arredondados para 4. O Banker's rounding é simétrico.

No Visual Basic for Applications, as seguintes funções numéricas executam o banker's rounding: CByte(), CInt(), CLng(), CCur() e Round().

Nenhuma função da planilha do Excel executa o banker's rounding.

Arredondamento aleatório

Até mesmo o banker's rounding pode influenciar no total. É possível usar a etapa extra para remover o ajuste arredondando .5 para mais ou para menos de uma maneira realmente aleatória. Deste modo, mesmo se os dados estiverem ajustados deliberadamente, o ajusto poderá ser menor. No entanto, o uso do arredondamento aleatório com dados distribuídos aleatoriamente pode resultar em um ajuste maior do que o banker's rounding. O arredondamento aleatório pode resultar em dois totais diferentes nos mesmos dados.

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

Arredondamento alternativo

O arredondamento alternativo ocorre entre .5 a mais e .5 a menos em chamadas sucessivas.

Nenhum produto Microsoft implementa um procedimento de arredondamento alternativo.

A Função Round() é implementada de maneira inconsistente

A função Round() não é implementada de uma maneira consistente entre os diferentes produtos Microsoft por razões históricas.

A tabela a seguir relaciona o produto para implementação:
   Produto                             Implementação
   ----------------------------------------------------------------------
   Visual Basic for Applications 6.0   Banker's Rounding
   Planilha do Excel                     Arredondamento aritmético simétrico
   SQL Server                          Arredondamento aritmético simétrico
                                       ou Arredondamento simétrico para menos (Correção)
                                       dependendo dos argumentos

   Biblioteca Java Math                   Arredondamento aritmético assimétrico
				

A função Round() no Visual Basic 6.0 e no Visual Basic for Applications 6.0 executa banker's rounding. Ele possui um segundo argumento opcional que especifica o número de dígitos decimais a serem arredondados:
   Debug.Print Round(2.45, 1) returns 2.4.
				

Dados de Exemplo

A seguinte tabela mostra alguns dados de exemplo e os efeitos dos diversos métodos de arredondamento no número e no total gerados.
   Number/Int./Fix/Ceiling/Asym. Arith./Sym. Arith./Banker's/Random/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:
   Number/Int./Fix/Ceiling/Asym. Arith./Sym. Arith./Banker's/Random/Alt.
   ---------------------------------------------------------------------
   0.0    -9   0   9       3            0           0        1      0
				

Total de todos os números negativos:
   Number/Int./Fix/Ceiling/Asym. Arith./Sym. Arith./Banker's/Random/Alt.
   ---------------------------------------------------------------------
   -13.5  -18  -9  -9      -12          -15         -13      -13    -14
				

Total de todos os números positivos:
   Number/Int./Fix/Ceiling/Asym. Arith./Sym. Arith./Banker's/Random/Alt.
   ---------------------------------------------------------------------
   13.5   9    9   18      15           15          13       14     14
				

A tabela mostra a diferença entre os diversos métodos de arredondamento. Para os números positivos e negativos distribuídos aleatoriamente, Fix(), arredondamento aritmético simétrico, banker's rounding e o arredondamento alternativo fornecem a menor diferença dos totais reais, com arredondamento aleatório.

No entanto, se os números forem todos positivos ou todos negativos, o banker's rounding, o arredondamento alternativo e o arredondamento aleatório fornecem a menor diferença dos totais reais.

Exemplo de funções de arredondamento definidas pelo usuário

O código de exemplo na seção Lista de função a seguir fornece exemplos de implementações para cada tipo de arredondamento descrito.

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

   SymDown       Arredonda simetricamente os números para menos - semelhante a Fix().
                 Trunca todos os números para 0.
                 Igual a AsymDown para números positivos.

   AsymUp        Arredonda assimetricamente as frações numéricas para mais.
                 Igual a SymDown para números negativos.
                 Semelhante a Ceiling.

   SymUp          Arredonda simetricamente as frações para mais - isto é, além de 0.
                 Igual a AsymUp para números positivos.
                 Igual a AsymDown para números negativos.

   AsymArith     Arredondamento aritmético assimétrico - arredonda .5 sempre para mais.
                 Semelhante à função Round da planilha do Java.

   SymArith      Arredondamento aritmético simétrico - arredonda .5 além de 0.
                 Igual a AsymArith para números positivos.
                 Semelhante à função Round da Planilha do Excel.

   BRound        Banker's rounding.
                 Arredonda .5 para mais ou para menos para chegar a um número par.
                 Simétrica por definição.

   RandRound     Arredondamento Aleatório.
                 Arredonda .5 para mais ou para menos de maneira aleatória.

   AltRound      Arredondamento alternativo.
                 Alterna entre arredondar .5 para mais ou para menos.

   ATruncDigits  Igual a AsyncTrunc, mas com argumentos diferentes.
				

Todas estas funções possuem dois argumentos: o número a ser arredondado e um fator opcional. Se o fator estiver omitido, as funções irão retornar um inteiro criado por um dos métodos acima. Se o fator estiver especificado, o número é escalado pelo fator 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.

OBSERVAÇÃO: Um fator de 0 gera um erro de tempo de execução: 1/factor = 1/0.

A seguinte tabela mostra os efeitos de diversos fatores:
   Expressão       Resultado  Comentário
   --------------------------------------------------------------------
   AsymArith(2.5)     3     Arredonda para mais ao próximo inteiro.
   BRound(2.18, 20)   2.2   Arredonda para próximo a 5 centavos (1/20 dólar) mais próximo.
   SymDown(25, .1)   20     Arredonda para menos a um múltiplo par de 10.
				

A exceção à descrição acima é ADownDigits, que é uma função de modelo, que permite a especificação do número de dígitos decimais em vez de um fator.
   Expressão       Resultado  Comentário
   ---------------------------------------------------------------------
   ADownDigits(2.18, 1)    2.1  Arredonda para menos ao próximo múltiplo de 10 ^ -1.
				

Lista de Função


   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
				

OBSERVAÇÃO: Com exceção da função MRound() da planilha do Excel, as funções de arredondamento interno possuem argumentos no modo de ADownDigits, em que o segundo argumento especifica o número de dígitos em vez de um fator.

As implementações de arredondamento apresentadas aqui usam um fator, como o MRound() mais flexível, pois não é necessário arredondar para 10. É possível gravar funções adicionais no modo ADownDigits.

Limitações do ponto flutuante

Todas as implementações de arredondamento apresentadas aqui, usam o tipo de dados double que pode representar aproximadamente 15 dígitos decimais.

Como nem todos os valores de fração podem ser expressos exatamente, talvez você tenha resultados inesperados, pois o valor de exibição não corresponde ao valor armazenado.

Por exemplo, o número 2.25 pode ser armazenado internamente como 2.2499999..., o que seria arredondado para menos com o arredondamento aritmético, não para mais como você esperava. Além disso, quanto mais cálculos forem feitos em um número, maior será a possibilidade do valor binário armazenado desviar do valor decimal ideal.

Se este for o caso, será necessário escolher um tipo de dados diferente, como Moeda, que é exato em 4 espaços decimais.

Você também pode criar os tipos de dados Variante e usar CDec() para converter tudo para o tipo de dado Decimal, que poderão ser de exatos 28 dígitos decimais.

Arredondamento dos valores de moeda

Ao usar o tipo de dado Moeda, que é de exatos 4 dígitos decimais, você geralmente precisa arredondar para dois dígitos decimais por centavos.

A função Round2CB abaixo é uma variação embutida em código que executa o banker's rounding para 2 dígitos decimais, mas não multiplica o número original. Isto evita uma possível condição de estouro se a quantia monetária estiver se aproximando 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

A seguir, está um exemplo do arredondamento aritmético assimétrico usando 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
				

Deixar a precisão como um atalho no arredondamento

Conforme foi ensinado na escola, o arredondamento é geralmente aritmético usando números positivos. Com este tipo de arredondamento, você precisa saber apenas o número de 1 dígito anterior ao que você está arredondando. Você ignora os dígitos anteriores ao primeiro espaço decimal. Em outras palavras, a precisão é colocada como um atalho para arredondar o valor.

Por exemplo, tanto 2.5 quanto 2.51 são arredondados para 3, enquanto 2.4 e 2.49 são arredondados para 2.

Quando você usa o banker's rounding (ou outros métodos que arredondam .5 para mais ou para menos) ou quando você arredonda números negativos usando o arredondamento aritmético assimétrico, deixar a precisão pode levar a resultados incorretos em que você precisará arredondar para o número mais próximo.

Por exemplo, com o banker's rounding, 2.5 arredonda para 2 e 2.51 arredonda para 3.

Com o arredondamento aritmético assimétrico, -2.5 arredonda para -2, enquanto -2.51 arredonda para -3.

As funções definidas pelo usuário apresentadas neste artigo levam em conta a precisão total do número ao executar o arredondamento.

Referências

Ajuda do Visual Basic, versão 6.0; tópico: Funções Int, Fix; Função Round

Ajuda do Microsoft Transact SQL; tópico: Função Round; função Floor; função Ceiling

(c) Microsoft Corporation 1998. Todos os Direitos Reservados. Contributions by Malcolm Stewart, Microsoft Corporation.

Propriedades

ID do artigo: 196652 - Última revisão: quarta-feira, 23 de agosto de 2006 - 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