Como posso 'aparar' um duplo C# para o valor que ele será armazenado como em um banco de dados SQLite?

StackOverflow https://stackoverflow.com/questions/4200736

Pergunta

Percebi que quando eu guardo um valor duplo, como por exemplo, x = 0.56657011973046234 em um banco de dados SQLite e depois recuperá -lo mais tarde, eu entendo y = 0.56657011973046201. De acordo com SQLITE SPEC e a .NET Spec (Nenhum dos quais eu originalmente me preocupei em ler :) Isso é esperado e normal.

Meu problema é que, embora a alta precisão não seja importante, meu aplicativo lida com os usuários inserindo/selecionando duplas que representam informações 3D básicas e, em seguida, executando simulações neles para encontrar um resultado. E essa entrada pode ser salva em um banco de dados SQLite a ser recarregado e re-executivo posteriormente.

A confusão ocorre porque uma série de entradas criada recém -criada obviamente simulará de maneira ligeiramente diferente para as mesmas entradas, uma vez armazenadas e recarregadas (como os valores duplos mudaram). Isso é lógico, mas não desejável.

Eu não cheguei bastante em como lidar com isso, mas, enquanto isso, gostaria de limitar/prender as entradas do usuário aos valores que podem ser exatamente armazenados em um banco de dados SQLite. Então, se um usuário entra 0.56657011973046234, na verdade é transformado em 0.56657011973046201.

No entanto, não consegui descobrir, dado um número, que valor seria armazenado no banco de dados, com falta de armazená -lo e recuperá -lo do banco de dados, que parece desajeitado. Existe uma maneira estabelecida de fazer isso?

Foi útil?

Solução

A Double Round possui uma implementação com um parâmetro que especifica o número de dígitos. Use isso para arredondar para 14 dígitos (digamos) com: rval = math.ound (val, 14)

Em seguida, ao redor ao receber o valor do banco de dados e no início das simulações, ou seja. Então, na correspondência dos valores?

Para detalhes:

http://msdn.microsoft.com/en-us/library/75ks3aby.aspx

Outro pensamento se você não estiver comparando valores no banco de dados, apenas armazenando -os: por que não simplesmente armazená -los como dados binários? Então todos os bits seriam armazenados e recuperados literalmente?

Outras dicas

A resposta pode ser para armazenar os valores duplos como 17 strings de dígitos significativos. Veja a diferença entre como o SQLite lida com números reais e texto (eu ilustrarei com a interface da linha de comando, por simplicidade):

sqlite> create table t1(dr real, dt varchar(25));
sqlite> insert into t1 values(0.56657011973046234,'0.56657011973046234');
sqlite> select * from t1;
0.566570119730462|0.56657011973046234

Armazenando -o com afinidade real é a causa do seu problema - o SQLite apenas oferece uma aproximação de 15 dígitos. Se você o armazenar como texto, poderá recuperar a string original com seu programa C# e convertê -lo novamente no duplo original.

Supondo que o SQL Lite e o .NET implementem corretamente a especificação do IEEE, você poderá obter os mesmos resultados numéricos se usar o mesmo tipo de ponto flutuante nos dois lados (porque o valor não deve ser alterado quando passado do banco de dados para C# e vice -versa).

Atualmente, você está usando o ponto flutuante IEEE de 8 bytes (único) (*) no SQL Lite e o ponto flutuante de 16 bytes em C# (duplo). o float Digite C# corresponde ao padrão IEEE de 8 bytes, portanto, usando este tipo em vez de double poderia resolver o problema.

(*) A documentação do SQL Lite diz que real é Um valor de ponto flutuante, armazenado como um número de ponto flutuante de 8 byte IEEE.

Você pode usar uma string para armazenar o # no banco de dados. Pessoalmente, fiz o que Winwaed sugeriu de arredondamento antes de armazenar e depois de buscar o banco de dados (que usava numeric ()).

Lembro -me de ter sido queimado por banqueiros arredondando, mas poderia ser que não tenha encontrado especificações.

Você pode armazenar o dobro como uma corda e, usando a formatação de ida e volta ao converter a dupla em uma string, é garantido gerar o mesmo valor quando analisado:

string formatted = theDouble.ToString("R", CultureInfo.Invariant);

Se você deseja que os valores de entrada decimal em ida e volta, precisará limitá-los a 15 dígitos significativos. Se você deseja os valores de ponto flutuante de precisão dupla interna do SQLite para viagem de ida e volta, pode estar sem sorte; Isso requer impressão para um mínimo de 17 dígitos significativos, mas pelo que posso dizer, o SQLite os imprime em um máximo de 15 (EDITAR: Talvez um especialista em sqlite possa confirmar isso? Acabei de ler o código -fonte e rastreá -lo - eu estava correto, a precisão é limitada a 15 dígitos.)

Eu testei seu exemplo na interface de comando sqlite no Windows. Inseri 0,56657011973046234 e selecionou retornou 0.566570119730462. Em C, quando atribuí 0,566570119730462 a um dobro e o imprimi para 17 dígitos, obtive 0,56657011973046201; Esse é o mesmo valor que você obtém de C#. 0,56657011973046234 e 0,56657011973046201 Mapeados para diferentes números de ponto flutuante; portanto, em outras palavras, o duplo SQLite não é uma viagem.

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