Qual é o melhor tipo de dados para usar em dinheiro em c #?

426

Qual é o melhor tipo de dados para usar em dinheiro em c #?

NotDan
fonte
4
Você pode encontrar respostas nesta postagem úteis.
Ntombela 28/03/09
Aqui está um mapeamento para todos os tipos de dados: docs.microsoft.com/en-us/dotnet/framework/data/adonet/…
JohnLBevan
Além disso, se estiver usando anotações de dados, inclua using System.ComponentModel.DataAnnotations;... [DataType(DataType.Currency)] msdn.microsoft.com/en-us/library/…
JohnLBevan

Respostas:

422

Como é descrito em decimal como:

A palavra-chave decimal indica um tipo de dados de 128 bits. Comparado aos tipos de ponto flutuante, o tipo decimal tem mais precisão e um intervalo menor, o que o torna apropriado para cálculos financeiros e monetários .

Você pode usar um decimal da seguinte maneira:

decimal myMoney = 300.5m;
Lee Treveil
fonte
41
Você deve explicar o que é esse link importante. Uma resposta deve ser suficientemente boa por si só, com um link como referência ou detalhe adicional. Veja stackoverflow.com/help/how-to-answer
TheRubberDuck 13/15
2
Portanto, a resposta de tamanho mínimo pode ter menos caracteres que o comentário de tamanho mínimo - interessante! Não que eu tenha um problema com a resposta concisa / concisa, especialmente quando ela também é "profunda", pois vincula a uma discussão mais aprofundada.
B. Clay Shannon
3
Resposta incrível, e não acho que precise de mais explicações, pois responde completamente à pergunta. O link para a documentação do MSDN é um bônus para mim. Bravo!
trnelson
@Leee Treveil, como é o dinheiro (9.0098) significa 4 caracteres após Point
SAR
114

System.Decimal

O tipo de valor decimal representa números decimais que variam de 79.228.162.514.264.337.593.543.950.335 positivo a 79.228.162.514.264.337.593.543.950.335 positivo. O tipo de valor decimal é apropriado para cálculos financeiros que exigem grandes números de dígitos integrais e fracionais significativos e sem erros de arredondamento. O tipo decimal não elimina a necessidade de arredondamento. Em vez disso, minimiza erros devido a arredondamentos.

Eu gostaria de apontar para esta excelente resposta do zneak sobre por que o dobro não deve ser usado.

David Walschots
fonte
68

Use o padrão Money de Patterns of Enterprise Application Architecture ; especifique o valor como decimal e a moeda como um enum.

lmsasu
fonte
2
Na verdade, eu sugeriria isso, mas faço da Currency uma classe para que eu possa definir uma taxa de câmbio (em relação a uma "moeda base", geralmente o dólar dos EUA [que eu defini para ter uma taxa de câmbio de 1,00]).
Thomas Owens
5
Para os futuros visitantes deste tópico (como eu), existe agora o seguinte: nuget.org/packages/Money e está ótimo !
Korijn 4/11/2014
Pensando se esse tipo deve ser uma estrutura ou classe. Um decimal + um (int) enum faz 20 bytes. Meu dinheiro ainda está em estrutura.
Nawfal
Esse Moneynuget tem um link morto do github para o site do projeto, então ... não há documentos?
George Mauer 02/04
O problema é que, se você estiver criando sua própria implementação, precisará descobrir como realmente persistir. E o ORM (EF) mais popular não tem suporte para tipos de dados personalizados. Portanto alguém é convidado a entrar realmente profundo no mato para fazer o que deve ser uma coisa bastante simples.
George Mauer
25

Decimal. Se você escolher o dobro, ficará aberto a erros de arredondamento

SquidScareMe
fonte
8
O @Jess doublepode introduzir erros de arredondamento porque o ponto flutuante não pode representar todos os números exatamente (por exemplo, 0,01 não tem representação exata no ponto flutuante). Decimal, Por outro lado, não representam números exatamente . (O trade-off Decimaltem um alcance menor que o ponto flutuante) O ponto flutuante pode gerar erros de arredondamento * inadvertidos * (por exemplo 0.01+0.01 != 0.02). Decimalpode dar-lhe erros de arredondamento, mas só quando você pediu para ele (por exemplo, Math.Round(0.01+0.02)retorna zero)
Ian Boyd
2
@IanBoyd: O valor "$ 1,57" pode ser representado com precisão (dobro) 157. Se alguém usar doublee aplicar cuidadosamente a escala e o arredondamento específico do domínio, quando apropriado, poderá ser perfeitamente preciso. Se alguém é desleixado em seu arredondamento, decimalpode produzir resultados que são semanticamente incorretos (por exemplo, se somarmos vários valores que deveriam ser arredondados para o centavo mais próximo, mas na verdade não os rodeiam primeiro). A única coisa boa decimalé que o dimensionamento é incorporado.
Supercat
1
@ supercat, em relação a este comentário "se alguém acrescentar vários valores que deveriam ser arredondados para o centavo mais próximo, mas que não estão à sua volta primeiro", não vejo como um flutuador resolveria isso. É um erro do usuário e não tem nada a ver com decimais IMHO. Eu entendi, mas acho que foi extraviado, principalmente porque IanBoyd especificou isso ... se você pedir.
sawe
13

Concorde com o padrão Money: o manuseio de moedas é muito complicado quando você usa decimais.

Se você criar uma classe Currency, poderá colocar toda a lógica relacionada ao dinheiro, incluindo um método ToString () correto, mais controle dos valores de análise e melhor controle das divisões.

Além disso, com uma classe Currency, não há chance de misturar dinheiro sem intenção com outros dados.

Lennaert
fonte
10

Outra opção (especialmente se você está rolando sua própria classe) é usar um int ou um int64 e designar os quatro dígitos inferiores (ou possivelmente até 2) como "à direita do ponto decimal". Então, "nas bordas", você precisará de "* 10000" na entrada e "/ 10000" na saída. Esse é o mecanismo de armazenamento usado pelo SQL Server da Microsoft, consulte http://msdn.microsoft.com/en-au/library/ms179882.aspx

A importância disso é que todo o seu somatório pode ser feito usando aritmética inteira (rápida).

dsz
fonte
7

A maioria dos aplicativos com os quais trabalhei usam decimalpara representar dinheiro. Isso se baseia no pressuposto de que o aplicativo nunca se preocupará com mais de uma moeda.

Essa suposição pode basear-se em outra suposição, de que o aplicativo nunca será usado em outros países com moedas diferentes. Já vi casos em que isso provou ser falso.

Agora essa suposição está sendo desafiada de uma nova maneira: novas moedas como Bitcoin estão se tornando mais comuns e não são específicas para nenhum país. Não é irreal que um aplicativo usado em apenas um país ainda precise suportar várias moedas.

Algumas pessoas dirão que criar ou mesmo usar um tipo apenas por dinheiro é "ouro", ou acrescenta complexidade extra além dos requisitos conhecidos. Eu discordo fortemente. Quanto mais onipresente estiver um conceito no seu domínio, mais importante será fazer um esforço razoável para usar a abstração correta antecipadamente. Se você quiser ver a complexidade, tente trabalhar em um aplicativo que costumava usar decimale agora há uma Currencypropriedade adicional ao lado de cada decimalpropriedade.

Se você usar a abstração errada no início, substituí-la mais tarde será cem vezes mais trabalhos. Isso significa potencialmente introduzir defeitos no código existente, e a melhor parte é que esses defeitos provavelmente envolverão quantias em dinheiro, transações com dinheiro ou apenas qualquer coisa com dinheiro.

E não é tão difícil usar algo que não seja decimal. Google "nuget money type" e você verá que vários desenvolvedores criaram tais abstrações (inclusive eu.) É fácil. É tão fácil quanto usar, em DateTimevez de armazenar uma data em um arquivo string.

Scott Hannen
fonte
5

Crie sua própria classe. Isso parece estranho, mas um tipo .Net é inadequado para cobrir diferentes moedas.

Noel Kennedy
fonte