Pergunta

Parece o tipo de código que só falha in-situ, mas tentarei adaptá-lo em um trecho de código que represente o que estou vendo.

float f = myFloat * myConstInt; /* Where myFloat==13.45, and myConstInt==20 */
int i = (int)f;
int i2 = (int)(myFloat * myConstInt);

Depois de percorrer o código, i==269 e i2==268.O que está acontecendo aqui para explicar a diferença?

Foi útil?

Solução

A matemática de flutuação pode ser realizada com maior precisão do que o anunciado. Mas assim que você o armazena em Float F, essa precisão extra é perdida. Você não está perdendo essa precisão no segundo método até que, é claro, você lançou o resultado para a Int.

Editar: veja esta pergunta Por que difere a precisão do ponto flutuante em C# quando separado por paranteses e quando separado por declarações? Para uma explicação melhor do que eu provavelmente forneci.

Outras dicas

Como as variáveis ​​de ponto flutuante não são infinitamente preciso.Use um decimal se precisar desse tipo de precisão.

Diferente modos de arredondamento também pode estar relacionado a esse problema, mas o problema de precisão é o que você está enfrentando aqui, AFAIK.

O ponto flutuante tem precisão limitado e é baseado em binário e não decimal. O número decimal 13.45 não pode ser representado com precisão no ponto flutuante binário, portanto, redondo. A multiplicação em 20 exagera ainda mais a perda de precisão. Neste ponto, você tem 268.999 ... - não 269 - Portanto, a conversão para truncados inteiros para 268.

Para obter arredondamento para o número inteiro mais próximo, você pode tentar adicionar 0,5 antes de converter de volta ao número inteiro.

Para a aritmética "perfeita", você pode tentar usar um tipo numérico decimal ou racional - acredito que C# possui bibliotecas para ambos, mas não tenho certeza. Estes serão mais lentos, no entanto.

EDITAR - Encontrei um tipo "decimal" até agora, mas não um racional - posso estar errado sobre isso estar disponível. O ponto flutuante decimal é impreciso, como binário, mas é o tipo de imprecisão que estamos acostumados, por isso fornece resultados menos surpreendentes.

Substituir com

double f = myFloat * myConstInt;

E veja se você obtém a mesma resposta.

Eu gostaria de oferecer uma explicação diferente.

Aqui está o código, que anotei (procurei na memória para dissecar os carros alegóricos):

 float myFloat = 13.45; //In binary is 1101.01110011001100110011
 int myConstInt = 20;
 float f = myFloat * myConstInt; //In binary is exactly 100001101 (269 decimal)
 int i = (int)f; // Turns float 269 into int 269 -- no surprises
 int i2 = (int)(myFloat * myConstInt);//"Extra precision" causes round to 268

Vamos olhar mais de perto os cálculos:

  • f = 1101.01110011001100110011 * 10100 = 100001100.1111111111111111 111 111

    A peça após o espaço é de 25-27, que fazem com que 24 sejam arredondados e, portanto, todo o valor será arredondado para 269

  • int i2 = (int) (myfloat * myconstint)

    O MyFloat é estendido para a dupla precisão para o cálculo (0s são anexados): 1101.0111001100110011001100100000000000000000000000000000

    myfloat * 20 = 100001100.111111111111111111100000000000000000000000000

    Os bits 54 e além são 0s, portanto, nenhum arredondamento é feito: o elenco resulta no número inteiro 268.

    (Uma explicação semelhante funcionaria se a precisão estendida fosse usada.)

Atualização: refinei minha resposta e escrevi um artigo completo chamado Quando os carros alegóricos não se comportam como carros alegóricos

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