Por que o .NET usa o arredondamento de banqueiros como padrão?

271

De acordo com a documentação, o decimal.Roundmétodo usa um algoritmo de arredondamento para o mesmo que não é comum na maioria dos aplicativos. Por isso, sempre acabo escrevendo uma função personalizada para executar o algoritmo round-half-up mais natural:

public static decimal RoundHalfUp(this decimal d, int decimals)
{
    if (decimals < 0)
    {
        throw new ArgumentException("The decimals must be non-negative", 
            "decimals");
    }

    decimal multiplier = (decimal)Math.Pow(10, decimals);
    decimal number = d * multiplier;

    if (decimal.Truncate(number) < number)
    {
        number += 0.5m;
    }
    return decimal.Round(number) / multiplier;
}

Alguém sabe o motivo por trás dessa decisão de design da estrutura?

Existe alguma implementação interna do algoritmo round-half-up na estrutura? Ou talvez alguma API do Windows não gerenciada?

Pode ser enganoso para iniciantes que simplesmente escrevem decimal.Round(2.5m, 0)esperando 3 como resultado, mas obtendo 2.

Darin Dimitrov
fonte
105
Arredondar para cima não é "mais natural". A natureza não tem nada a ver com isso. É simplesmente o que você aprendeu na escola quando aprendeu o conceito de "arredondamento". As aulas da escola nem sempre pintam uma imagem completa.
Rob Kennedy
45
@ Rob e é por isso que é mais naturais , mesmo que ele não é correta
Pacerier
10
Eu não entendo, @Pacerier. Expliquei por que não é natural, e você diz que é de fato por que é natural. Como meu argumento funciona contra a minha conclusão, que é o oposto do seu? Coisas com as quais você se acostumou podem parecer naturais, e às vezes dizemos figurativamente que algo é "segunda natureza", mas isso não as torna naturais.
Rob Kennedy
16
@ Rob Estou dizendo que é natural, porque parece natural. Você sabe que existem 36 objetos diferentes com o mesmo nome da variável natural, certo?
Pacerier 18/10/11
9
a natureza é definitivamente análoga, portanto é a palavra errada a ser usada; mas isso está sendo pedante. Talvez 'usual' seja uma palavra melhor para usar. "Qual é o arredondamento usual que as pessoas fazem"> 0,5 vai para 1,0
whytheq

Respostas:

196

Provavelmente porque é um algoritmo melhor. No decorrer de muitos arredondamentos realizados, você calculará a média de que todos os 0,5 terminam arredondando igualmente para cima e para baixo. Isso fornece melhores estimativas dos resultados reais, se você estiver, por exemplo, adicionando vários números arredondados. Eu diria que, embora não seja o que alguns possam esperar, é provavelmente a coisa mais correta a se fazer.

Kibbee
fonte
71
supondo que você tenha uma distribuição plana de entradas pares e ímpares, é claro
jk.
7
+1 para melhor algoritmo , embora Ostemar tem a real resposta ( stackoverflow.com/questions/311696/... )
Ian Boyd
2
@Ian, dou a resposta +1 também. De qualquer forma, podemos obter a "resposta aceita movida" Talvez o OP possa fazer isso. A resposta real para "por que" ele usa esse método está na metade da página. Embora eu goste bastante do aumento de representantes, recebo uma vez por semana com esta resposta.
Kibbee
@ Kibbee - obrigado por empurrar minha resposta. Eu acho que cabe ao OP alterar a resposta aceita como achar melhor?
Ostemar 25/09
-1 por afirmar que é um algoritmo melhor. - Dada uma amostra aleatória de número usando o arredondamento de banca, você acabará tendo mais números em posições pares do que em posições ímpares. - Somente depois da média desses números é que você obtém novamente um spread semelhante à distribuição original. - No entanto, se você, por exemplo, plotar esses dados em um gráfico de dispersão, poderá ver um agrupamento artificial.
precisa saber é
437

As outras respostas, com razões pelas quais o algoritmo do Banker (também conhecido como metade da rodada até o par ) é uma boa opção, estão bem corretas. Ele não sofre viés negativo ou positivo, tanto quanto a metade da metade do método zero , sobre a maioria das distribuições razoáveis.

Mas a pergunta era por que o .NET usa o arredondamento real do Banker como padrão - e a resposta é que a Microsoft seguiu o padrão IEEE 754 . Isso também é mencionado no MSDN for Math.Round em Comentários.

Observe também que o .NET suporta o método alternativo especificado pelo IEEE, fornecendo a MidpointRoundingenumeração. É claro que eles poderiam ter fornecido mais alternativas para resolver laços, mas optam apenas por cumprir o padrão IEEE.

Ostemar
fonte
17
Então, por que o IEEE 754 segue o arredondamento dos banqueiros? Essa (ainda boa) resposta apenas passa do balde.
Henk Holterman 14/03
4
@HenkHolterman Provavelmente devido ao que é mencionado nas outras respostas (e como resumi); ele não sofre (muito) de viés negativo ou positivo e, como tal, cria um padrão mais ressonável para a maioria das distribuições e domínios problemáticos.
Ostemar
1
Related: Mathematica.SE: Por que arredondar para números pares?
precisa saber é o seguinte
Eu acho interessante porque o IEEE 754 é o padrão para números de ponto flutuante dos quais o Decimal não é. Talvez siga IEEE 754 para que o mesmo algoritmo seja usado para arredondar o dobro como decimal.
Brandon Barkley #
1
@BrandonBarkley Um decimal ou decimal é um número de ponto flutuante e o IEEE 754 inclui números de ponto flutuante decimal .
21418 Pablo H:
88

Embora eu não possa responder à pergunta "Por que os designers da Microsoft escolheram isso como padrão?", Só quero salientar que uma função extra é desnecessária.

Math.Roundpermite que você especifique um MidpointRounding:

  • ToEven - Quando um número está na metade do caminho entre dois outros, é arredondado para o número par mais próximo.
  • AwayFromZero - Quando um número está na metade do caminho entre dois outros, é arredondado para o número mais próximo que estiver longe de zero.
Michael Stum
fonte
8
E, como mencionei em threads relacionados, verifique se você é consistente no arredondamento - se você às vezes faz o arredondamento no banco de dados e, às vezes, no .net, terá erros estranhos de um centavo que levarão semanas para descobrir.
chris
59
Uma vez, um cliente me pagou mais de US $ 40.000 para rastrear um erro de arredondamento de US $ 0,11 entre dois números que tinham apenas 1 bilhão de dólares; o valor de US $ 0,11 ocorreu devido a uma diferença no erro de arredondamento do 8º dígito entre um mainframe e o SQL Server. Fale sobre um perfeccionista!
EJ Brennan
12
@EJB - Eu provavelmente seria um perfeccionista se eu estava lidando com um bilhão de dólares ;-)
royse41
12
@EJ Brennan: Foram necessários US $ 40 mil para descobrir isso? Vejo problemas como esse o tempo todo, o arredondamento é a causa nº 1, a normalização dupla / flutuante é a causa nº 2, o erro do programador nº 3 a nº 3 pode ser imediatamente definido como nº 1 se não houver casos de teste predefinidos. btw, por favor, pode me colocar em contato com seu cliente bilionário, acho que também poderia encontrar mais alguns bugs de $ 40k no sistema dele! : D
3
@seanxe: Ou se você tivesse visto o Office Space. Sério, sempre que você vê pequenas e misteriosas imprecisões no dinheiro, resolver o mistério de exatamente como elas estão acontecendo é quase sempre uma boa idéia. É possível que você decida não corrigir bugs, mas saber a causa subjacente ainda tem valor. Aposto que muitas pessoas que trabalham com dinheiro ficam felizes em notar até pequenas imprecisões.
18711 Brian
22

Os decimais são usados ​​principalmente para dinheiro ; O arredondamento do banqueiro é comum quando se trabalha com dinheiro . Ou você poderia dizer.

São principalmente os banqueiros que precisam do tipo decimal; portanto, faz o "arredondamento do banqueiro"

Os arredondamentos dos banqueiros têm a vantagem de que, em média, você obterá o mesmo resultado se:

  • arredondar um conjunto de "linhas da fatura" antes de adicioná-las,
  • ou adicioná-los e arredondar o total

O arredondamento antes da adição economizava muito trabalho nos dias anteriores aos computadores.

(No Reino Unido, quando entramos em bancos decimais, não lidamos com meia moeda, mas por muitos anos ainda havia uma moeda de meia moeda e a loja geralmente tinha preços que terminavam em meia moeda - muito arredondamento)

Ian Ringrose
fonte
8
"Os decimais são usados ​​principalmente para dinheiro" ... e tudo o mais que não é um número inteiro.
John Tyree
3
@ JohnTyree, não é verdade na maioria das vezes um double / float é usado quando não é um número inteiro. veja stackoverflow.com/questions/2545567/…
Ian Ringrose
3
Uau. Erro ridículo da minha parte. Decmials sim Decimais, não. Para a posteridade, concordo com o sentimento original aqui.
precisa
Os banqueiros podem gostar do arredondamento dos banqueiros, mas os contadores podem não ser fãs dele, eles dizem que a diferença de 0,005 deve arredondar para levar ao arredondamento em 0,01, não dependendo se é um número ímpar ou par.
JustAMartin
0

Use outra sobrecarga da função Round como esta:

decimal.Round(2.5m, 0,MidpointRounding.AwayFromZero)

Ele produzirá 3 . E se você usar

decimal.Round(2.5m, 0,MidpointRounding.ToEven)

você receberá o arredondamento dos banqueiros.

Omid Sadeghi
fonte
4
Isso não responde à pergunta de por que o arredondamento do Banker foi escolhido como padrão.
shortstuffsushi