Pergunta

Eu queria saber se existe uma maneira de superar um problema de precisão que parece ser o resultado da representação interna da minha máquina dos números de ponto flutuante:

Por uma questão de clareza, o problema está resumido como:

// str is "4.600";   atof( str ) is 4.5999999999999996  
double mw = atof( str )  

// The variables used in the columns calculation below are:   
//  
//                    mw = 4.5999999999999996  
//                    p = 0.2  
//                    g = 0.2  
//                    h = 1 (integer)  

int columns = (int) ( ( mw - ( h * 11 * p ) ) / ( ( h * 11 * p ) + g ) ) + 1;

Antes de fundir para um tipo inteiro, o resultado do cálculo das colunas é 1.9999999999999996; tão perto, mas tão longe do resultado desejado de 2.0.

Quaisquer sugestões mais bem -vindas.

Foi útil?

Solução

Uma maneira muito simples e eficaz de arredondar um número de ponto flutuante para um número inteiro:

int rounded = (int)(f + 0.5);

Nota: Isso só funciona se f é sempre positivo. (obrigado J Random Hacker)

Outras dicas

Quando você usa o ponto aritmético de ponto flutuante, a igualdade estrita é quase sem sentido. Você geralmente deseja comparar com uma variedade de valores aceitáveis.

Observe que alguns valores podem não ser representado exatamente como vlues de ponto flutuante.

Ver O que todo cientista da computação deve saber sobre aritmética de ponto flutuante e Comparando números de ponto flutuante.

Não há problema de precisão.

O resultado que você obteve (1.99999999999999996) diferiu do resultado matemático (2) por uma margem de 1E-16. Isso é bastante preciso, considerando sua entrada "4.600".

Você tem um problema de arredondamento, é claro. O arredondamento padrão em C ++ é o truncamento; Você quer algo semelhante à solução de Kip. Os detalhes dependem do seu domínio exato, você espera round(-x)== - round(x) ?

Se você não leu, o título de este papel está realmente correto. Por favor, considere lê-lo, para saber mais sobre os fundamentos da aritmética de ponto flutuante em computadores modernos, algumas armadilhas e explicações sobre o motivo pelo qual eles se comportam da maneira que fazem.

Se a precisão for realmente importante, considere usar números de ponto flutuante de precisão dupla, em vez de apenas ponto flutuante. Embora a sua pergunta pareça que você já é. No entanto, você ainda tem um problema em verificar valores específicos. Você precisa de código nas linhas de (supondo que esteja verificando seu valor contra zero):

if (abs(value) < epsilon)
{
   // Do Stuff
}

onde "epsilon" é um valor pequeno, mas não zero.

Nos computadores, os números de ponto flutuante nunca são exatos. Eles são sempre apenas uma aproximação próxima. (1E-16 está próximo.)

Às vezes, existem pedaços ocultos que você não vê. Às vezes, as regras básicas da álgebra não se aplicam mais: a*b! = B*a. Às vezes, comparar um registro com a memória mostra essas diferenças sutis. Ou usando um coprocessador de matemática vs uma biblioteca de pontos flutuantes de tempo de execução. (Eu tenho feito isso Waayyy também há muito tempo.)

C99 define: (olhe math.h)

double round(double x);
float roundf(float x);
long double roundl(long double x);

.

Ou você pode rolar o seu próprio:

template<class TYPE> inline int ROUND(const TYPE & x)
{ return int( (x > 0) ? (x + 0.5) : (x - 0.5) ); }

Para equivalência de ponto flutuante, tente:

template<class TYPE> inline TYPE ABS(const TYPE & t)
{ return t>=0 ? t : - t; }

template<class TYPE> inline bool FLOAT_EQUIVALENT(
    const TYPE & x, const TYPE & y, const TYPE & epsilon )
{ return ABS(x-y) < epsilon; }

Use decimais: Decnumber ++

Você pode ler isso papel Para encontrar o que você está procurando.

Você pode obter o valor absoluto do resultado como visto aqui:

x = 0.2;  
y = 0.3;  
equal = (Math.abs(x - y) < 0.000001)  
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top