Pergunta

Eu escrever aplicações financeiras onde eu constantemente combater a decisão de usar um duplo vs usando um decimal.

Toda a minha matemática funciona em números com não mais de 5 casas decimais e não são maiores do que ~ 100.000. Tenho a sensação de que tudo isso pode ser representado como duplos de qualquer maneira sem arredondamento erro, mas nunca foram certeza.

Gostaria de ir em frente e fazer a troca de decimais a duplos para a vantagem da velocidade óbvio, exceto que no final do dia, eu ainda usar o método ToString para preços transmitir aos intercâmbios e necessidade de se certificar de que sempre saídas o número que eu esperava. (89.99 em vez de 89,99000000001)

Perguntas:

  1. É a vantagem da velocidade realmente tão grande como testes ingênuos sugere? (~ 100 vezes)
  2. Existe uma maneira de garantir a saída de ToString para ser o que eu quero? É este assegurado pelo fato de que meu número é sempre representável?

UPDATE: Eu tenho que processo ~ 10 bilhões de atualizações de preços antes de meu aplicativo pode ser executado, e tenho implementado com decimal agora pelos motivos de proteção evidentes, mas é preciso ~ 3 horas apenas para ligar, duplos iria reduzir drasticamente o meu transformar no tempo. Existe uma maneira segura de fazê-lo com duplos?

Foi útil?

Solução

  1. aritmética de ponto flutuante será quase sempre significativamente mais rápido porque ele é suportado diretamente pelo hardware. Até agora, quase nenhuma amplamente utilizados suportes de hardware decimal aritmética (embora isso está mudando, ver comentários).
  2. As aplicações financeiras deveriam sempre números uso decimais, o número de histórias de terror decorrentes do uso de ponto flutuante em aplicações financeiras é interminável, você deve ser capaz de encontrar muitos exemplos com uma pesquisa do Google.
  3. Enquanto decimal aritmética pode ser significativamente mais lento do que aritmética de ponto flutuante, a menos que você está gastando uma quantidade significativa de tempo de processamento de dados decimais o impacto sobre o seu programa é susceptível de ser desprezível. Como sempre, fazer o profiling apropriado antes de começar a se preocupar com a diferença.

Outras dicas

Há duas questões separáveis ??aqui. Uma delas é se o casal tem precisão suficiente para manter todos os bits que você precisa, eo outro é onde ele pode representar os números exatamente.

Como para a representação exata, você está certo de ser cauteloso, porque uma fração decimal exato como 1/10 não tem exata contrapartida binário. No entanto, se você sabe que você só precisa de 5 dígitos decimais de precisão, você pode usar escalado aritmética em que opera em números multiplicado por 10 ^ 5. Por exemplo, se você deseja representar 23,7205 exatamente você representá-lo como 2372050.

Vamos ver se há suficiente precisão: precisão dupla dá-lhe 53 bits de precisão. Isto é equivalente a mais de 15 dígitos decimais de precisão. Então, isso iria permitir-lhe cinco dígitos após o ponto decimal e 10 dígitos antes do ponto decimal, o que parece suficiente para sua aplicação.

Gostaria de colocar este código C em um arquivo .h:

typedef double scaled_int;

#define SCALE_FACTOR 1.0e5  /* number of digits needed after decimal point */

static inline scaled_int adds(scaled_int x, scaled_int y) { return x + y; }
static inline scaled_int muls(scaled_int x, scaled_int y) { return x * y / SCALE_FACTOR; }

static inline scaled_int scaled_of_int(int x) { return (scaled_int) x * SCALE_FACTOR; }
static inline int intpart_of_scaled(scaled_int x) { return floor(x / SCALE_FACTOR); }
static inline int fraction_of_scaled(scaled_int x) { return x - SCALE_FACTOR * intpart_of_scaled(x); }

void fprint_scaled(FILE *out, scaled_int x) {
  fprintf(out, "%d.%05d", intpart_of_scaled(x), fraction_of_scaled(x));
}

Há provavelmente alguns pontos ásperos, mas isso deve ser suficiente para você começar.

Sem sobrecarga para além disso, o custo de uma multiplicação ou duplas dividem.

Se você tem acesso a C99, você pode também tentar escalado inteiro aritmética usando o int64_t tipo inteiro de 64 bits. Que é mais rápido vai depender de sua plataforma de hardware.

Use sempre Decimal para quaisquer cálculos financeiros ou você vai ser para sempre perseguindo erros 1cent arredondamento.

  1. Sim; aritmética software realmente é 100 vezes mais lento do que hardware. Ou, pelo menos, é muito mais lento, e um fator de 100, mais ou menos uma ordem de magnitude, é sobre a direita. De volta aos velhos tempos quando você não podia supor que cada 80386 teve um co-processador 80387 de ponto flutuante, então você tinha simulação de software de ponto flutuante binário também, e isso era lento.
  2. Não; você está vivendo em uma terra de fantasia, se você acha que um ponto flutuante binário puro pode sempre exatamente representar todos os números decimais. números binários pode combinar metades, quartos, oitavos, etc, mas desde uma casa decimal exato de 0,01 requer dois fatores de um quinto e um fator de quarto (1/100 = (1/4) * (1/5) * (1 / 5)) e uma vez que um quinto não tem representação exata em binário, você não pode representar exatamente todos os valores decimais com valores binários (porque 0,01 é um contra-exemplo que não pode ser representado exatamente, mas é representativa de uma enorme classe de números decimais que não pode ser representado exatamente).

Então, você tem que decidir se você pode lidar com o arredondamento antes de chamar ToString () ou se você precisa encontrar algum outro mecanismo que irá lidar com arredondamento seus resultados como eles são convertidos para uma string. Ou você pode continuar a aritmética uso decimal, uma vez que continuará a ser precisa, e ele vai ficar mais rápido, uma vez máquinas são liberados que suporte o novo IEEE aritmética 754 decimal em hardware.

obrigatório referência cruzada: O que cada computador cientista deve saber Sobre Floating-Point Arithmetic . Essa é uma das muitas URLs possíveis.

As informações sobre a aritmética decimal eo novo IEEE 754:. Standard 2008 neste href="http://speleotrove.com/decimal/" rel="nofollow noreferrer"> local

Basta usar um longo e multiplique por uma potência de 10. Depois que você terminar, dividir pela mesma potência de 10.

Os decimais devem ser sempre usados ??para cálculos financeiros. O tamanho dos números não é importante.

A maneira mais fácil para mim explicar é através de algum código C #.

double one = 3.05;
double two = 0.05;

System.Console.WriteLine((one + two) == 3.1);

Esse trecho de código irá imprimir false , embora 3.1 é igual a 3.1 ...

A mesma coisa ... mas usando decimal:

decimal one = 3.05m;
decimal two = 0.05m;

System.Console.WriteLine((one + two) == 3.1m);

Isto irá agora imprimir true !

Se você quiser evitar esse tipo de problema, eu recomendo que você ficar com decimais.

refiro-me à minha resposta dada à esta questão .

Use um longo, armazenar a menor quantidade que você precisa para rastrear e exibir os valores em conformidade.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top