Como é que um C # avaliar ponto em pairar sobre e janela intermediária flutuante contra compilado?

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

  •  05-07-2019
  •  | 
  •  

Pergunta

Eu estou vendo algo estranho com o armazenamento de duplas em um dicionário, e estou confuso quanto ao porquê.

Aqui está o código:

            Dictionary<string, double> a = new Dictionary<string, double>();
            a.Add("a", 1e-3);

            if (1.0 < a["a"] * 1e3)
                Console.WriteLine("Wrong");

            if (1.0 < 1e-3 * 1e3)
                Console.WriteLine("Wrong");

O segundo se instrução funciona como esperado; 1,0 não é menor do que 1,0. Agora, os primeiro se avalia afirmação como verdadeira. A coisa muito estranha é que quando eu passar o mouse sobre o caso, o intellisense me diz que falsa, mas o código felizmente se move para a Console.WriteLine.

Isto é para C # 3.5 no Visual Studio 2008.

Este é um problema precisão de ponto flutuante? Então porque é que o segundo se o trabalho afirmação? Sinto que estou perdendo algo muito fundamental aqui.

Qualquer visão é apreciado.

Edit2 (Re-purposing questão um pouco):

Eu posso aceitar o problema de precisão matemática, mas a minha pergunta agora é: por que a pairar sobre avaliar adequadamente? Isto também é verdade para a janela intermediária. I colar o código a partir da primeira instrução if na janela intermediária e avalia falsa.

Atualizar

Antes de tudo, muito obrigado por todas as grandes respostas.

Eu também estou tendo problemas recriando isso em outro projeto na mesma máquina. Olhando para as configurações do projeto, não vejo diferenças. Olhando para o IL entre os projetos, não vejo diferenças. Olhando para a desmontagem, não vejo diferenças aparentes (além de endereços de memória). No entanto, quando eu depurar o projeto original, eu vejo: captura de tela do problema

A janela de imediato me diz o que é falso, mas o código cai na condicional.

De qualquer forma, a melhor resposta, penso eu, é se preparar para aritmética de ponto flutuante nestas situações. A razão que eu não poderia deixar isso ir tem mais a ver com os cálculos do depurador diferentes do tempo de execução. Então, muito obrigado a Brian Gideon e stephentyrone para alguns comentários muito perspicaz.

Foi útil?

Solução

Ele está flutuando problema de precisão.

Segundo declaração funciona porque as contagens de compilador a expressão 1e3 * 1e3 antes de emitir o .exe.

procurá-lo em ILDasm / refletor, ele irá emitir algo como

 if (1.0 < 1.0)
                Console.WriteLine("Wrong");

Outras dicas

O problema aqui é bastante sutil. O compilador C # não (sempre) código Emit que faz a computação em dobro, mesmo quando esse é o tipo que você especificou. Em particular, ele emite código que faz a computação em precisão "estendida" usando x87 instruções, sem arredondamento resultados intermediários para o dobro.

Dependendo se 1e-3 é avaliado como um casal ou long double e se a multiplicação é calculado em casal ou long double, é possível obter qualquer um dos três resultados seguintes:

  • (long double) 1e3 * 1e3 calculado em long double é de 1,0 - epsilon
  • (duplo) 1e3 * 1e3 calculado em dupla é exatamente 1.0
  • (duplo) 1e3 * 1e3 calculado em long double é de 1,0 + epsilon

Claramente, a primeira comparação, o que não está a cumprir as suas expectativas está sendo avaliada da maneira descrita no terceiro cenário I listados. 1e-3 está sendo arredondado para dobrar ou porque você está armazenando-lo e carregá-lo novamente, o que obriga o arredondamento, ou porque C # reconhece 1e-3 como um literal de precisão dupla e trata-lo dessa maneira. A multiplicação está sendo avaliada em long double porque C # tem uma morte cerebral modelo numerics essa é a maneira que o compilador está gerando o código.

A multiplicação no segundo comparador está, quer sendo avaliada utilizando um dos outros dois métodos, (É possível descobrir que ao tentar "1> * 1e3 1e3"), ou o compilador é arredondando o resultado da multiplicação antes de compará-lo com 1,0 quando se avalia a expressão em tempo de compilação.

É provável possível para você para dizer ao compilador para não usar precisão estendida sem você dizendo-lhe para via alguma configuração construção; habilitando CodeGen para SSE2 também pode funcionar.

Veja as respostas aqui

Umm ... estranho. Eu não sou capaz de reproduzir o seu problema. Eu estou usando C # 3.5 e Visual Studio 2008 também. I digitou no seu exemplo exatamente , uma vez que foi publicado e eu não estou vendo qualquer declaração Console.WriteLine executar.

Além disso, a segunda declaração se está sendo otimizado pelo compilador. Quando eu examinar tanto a depuração e compilações em ILDASM / refletor não vejo nenhuma evidência disso. Isso faz desde porque eu recebo um aviso do compilador dizendo código inacessível foi detectado nele.

Finalmente, eu não vejo como isso poderia ser um problema de ponto flutuante de precisão de qualquer maneira. Por que o compilador C # avaliar estaticamente duas duplas de forma diferente do que o faria CLR em tempo de execução? Se isso fosse realmente o caso, então se poderia fazer o argumento de que o compilador C # tem um bug.

Editar: Depois de dar este um pouco mais pensei que eu estou ainda mais convencido de que este é não uma questão de ponto flutuante de precisão. Você deve ter tropeçou em um bug no compilador ou o depurador ou o código que você postou não é exatamente representante do seu código real que está sendo executado. Eu sou altamente cético em relação a um bug no compilador, mas um bug no depurador parece mais provável. Tente reconstruir o projeto e executá-lo novamente. Talvez as informações de depuração compilados junto com exe ficou fora de sincronia ou algo assim.

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