Qual é a diferença entre flutuar e dobrar?

Li sobre a diferença entre precisão dupla e precisão única. No entanto, na maioria dos casos, float e double parecem ser permutáveis, isto é, utilizar um ou outro não parece afectar os resultados. É mesmo assim? Quando é que carros alegóricos e duplos são intercambiáveis? Quais são as diferenças entre eles?

Author: nbro, 2010-03-05

11 answers

Grande diferença.

Como o nome indica, um double tem 2x a precisão de float[1]. Em geral, a double tem 15 dígitos decimais de precisão, enquanto float tem 7.

Aqui está como o número de dígitos são calculados:

double tem 52 Bits de mantissa + 1 bits escondidos: Registo(253)÷log (10) = 15.95 dígitos

float tem 23 bits de mantissa + 1 bits escondidos: Registo(224)÷log (10) = 7.22 dígitos

Esta perda de precisão pode levar a erros de truncamento muito mais fáceis de flutuar, por exemplo

float a = 1.f / 81;
float b = 0;
for (int i = 0; i < 729; ++ i)
    b += a;
printf("%.7g\n", b); // prints 9.000023

Enquanto

double a = 1.0 / 81;
double b = 0;
for (int i = 0; i < 729; ++ i)
    b += a;
printf("%.15g\n", b); // prints 8.99999999999996

Também, o valor máximo do flutuador é cerca de 3e38, mas o dobro é cerca de 1.7e308, por isso o uso de float pode atingir "infinito" (ou seja, um número especial de ponto flutuante) muito mais facilmente do que double para algo simples, por exemplo, computando o factorial de 60.

Durante os testes, talvez alguns casos de teste contenham estes números enormes, o que pode causar os programas falharão se usar flutuadores.

É claro que, às vezes, até mesmo double não é preciso o suficiente, daí que, por vezes, ter long double[1] (o exemplo acima dá 9.000000000000000066 no Mac), mas todos os tipos de ponto flutuante sofrem de erros de arredondamento, então, se a precisão é muito importante (e.g. dinheiro de processamento) deve utilizar int ou uma fração de classe.


Além disso, não use += para somar muitos números de vírgula flutuante, pois os erros acumulam-se. rapidamente. Se estiver a utilizar Python, use fsum. Caso contrário, tente implementar o algoritmo De soma de Kahan.

[1]: as normas C E C++ não especificam a representação de float, double e long double É possível que todos os três sejam implementados como IEEE dupla precisão. No entanto, para a maioria das arquiteturas (gcc, MSVC; x86, x64, ARM) float é De facto um número de vírgula flutuante de precisão única (binary32), e double is a IEEE número de vírgula flutuante de precisão dupla (binary64).

 407
Author: kennytm, 2018-03-10 10:55:48

Eis o que a norma C99 (ISO-IEC 9899 6.2.5 §10) ou C++2003 (ISO-IEC 14882-2003 3.1.9 §8) diz:

Existem três tipos de vírgula flutuante: float, double, e long double O tipo double fornece pelo menos tanta precisão como float, e o tipo long double fornece pelo menos tanta precisão como double. O conjunto de valores do tipo float é um subconjunto do conjunto de valores do tipo double; o conjunto de valores do tipo double é um subconjunto do conjunto de valores de o tipo long double.

A norma c++ adiciona:

A representação dos valores dos tipos de vírgula flutuante é definida pela aplicação.

[[12]} eu sugeriria dar uma olhada no excelente O Que todo cientista da computação deve saber sobre aritmética de ponto flutuante {[[26]} que cobre o padrão de ponto flutuante IEEE em profundidade. Você vai aprender sobre os detalhes da representação e você vai perceber que há uma troca entre magnitude e precisão. Precisao da representação de vírgula flutuante aumenta à medida que a magnitude diminui, daí os números de vírgula flutuante entre -1 e 1 são aqueles com maior precisão.
 50
Author: Gregory Pakosz, 2010-03-06 17:29:32

Dada uma equação quadrática: x2 - 4.0000000 x + 3.9999999 = 0, a exata raízes para 10 dígitos significativos são, r1 = 2.000316228 e r2 = 1.999683772.

Usando float e double, podemos escrever um programa de teste:

#include <stdio.h>
#include <math.h>

void dbl_solve(double a, double b, double c)
{
    double d = b*b - 4.0*a*c;
    double sd = sqrt(d);
    double r1 = (-b + sd) / (2.0*a);
    double r2 = (-b - sd) / (2.0*a);
    printf("%.5f\t%.5f\n", r1, r2);
}

void flt_solve(float a, float b, float c)
{
    float d = b*b - 4.0f*a*c;
    float sd = sqrtf(d);
    float r1 = (-b + sd) / (2.0f*a);
    float r2 = (-b - sd) / (2.0f*a);
    printf("%.5f\t%.5f\n", r1, r2);
}   

int main(void)
{
    float fa = 1.0f;
    float fb = -4.0000000f;
    float fc = 3.9999999f;
    double da = 1.0;
    double db = -4.0000000;
    double dc = 3.9999999;
    flt_solve(fa, fb, fc);
    dbl_solve(da, db, dc);
    return 0;
}  
Executar o programa dá-me ...
2.00000 2.00000
2.00032 1.99968

Note que os números não são grandes, mas ainda assim você tem efeitos de cancelamento usando float.

(Na verdade, o acima não é o a melhor maneira de resolver equações quadráticas usando números de ponto flutuante de precisão simples ou dupla, mas a resposta permanece inalterada mesmo que se utilize um método mais estável.)

 24
Author: Alok Singhal, 2010-03-05 17:57:25
  • um duplo é 64 e uma única precisão (float) é de 32 bits.
  • o duplo tem um mantissa maior (os bits inteiros do número real).
  • qualquer imprecisão será menor no duplo.
 18
Author: graham.reeds, 2010-03-05 12:53:20

O tamanho dos números envolvidos nos cálculos do ponto flutuante não é a coisa mais relevante. É o cálculo que está sendo realizado que é relevante.

Em essência, se você está realizando um cálculo e o resultado é um número irracional ou recorrente decimal, então haverá erros de arredondamento quando esse número é esmagado na estrutura de dados de tamanho finito que você está usando. Uma vez que o dobro é o dobro do tamanho do flutuador, então o erro de arredondamento será muito inferior.

Os testes podem usar números específicos que causariam este tipo de erro e, portanto, testaram que tinha usado o tipo apropriado no seu código.

 11
Author: Dolbz, 2018-03-10 11:05:03
Os flutuadores têm menos precisão que os duplos. Embora você já saiba, leia O que devemos saber sobre aritmética de ponto flutuante para melhor compreensão.
 8
Author: N 1.1, 2010-03-05 13:09:51

O tipo flutuante, com 32 bits de comprimento, tem uma precisão de 7 dígitos. Embora possa armazenar valores com uma gama muito grande ou muito pequena (+/- 3.4 * 10^38 ou * 10^-38), tem apenas 7 dígitos significativos.

O tipo duplo, com 64 bits de comprimento, tem uma gama maior (*10^+/-308) e uma precisão de 15 dígitos.

O tipo duplo longo é nominalmente de 80 bits, embora um dado compilador / os emparelhamento possa armazená-lo como 12-16 bytes para fins de alinhamento. O duplo longo tem um expoente que apenas ridiculamente enorme e deve tem 19 dígitos de precisão. A Microsoft, na Sua infinita sabedoria, limita o dobro a 8 bytes, o mesmo que o duplo simples.

De um modo geral, basta usar o tipo duplo quando precisar de um valor/variável de vírgula flutuante. Valores literais de vírgula flutuante usados em expressões serão tratados como duplos por padrão, e a maioria das funções matemáticas que retornam valores de vírgula flutuante retornam duplos. Poupas muitas dores de cabeça e digestões se usares o dobro.

 8
Author: Humble Coder, 2017-11-17 23:29:38
Encontrei um erro que me levou uma eternidade a descobrir e que pode dar-lhe um bom exemplo de precisão de flutuador.
#include <iostream>
#include <iomanip>

int main(){
  for(float t=0;t<1;t+=0.01){
     std::cout << std::fixed << std::setprecision(6) << t << std::endl;
  }
}

A saída é

0.000000
0.010000
0.020000
0.030000
0.040000
0.050000
0.060000
0.070000
0.080000
0.090000
0.100000
0.110000
0.120000
0.130000
0.140000
0.150000
0.160000
0.170000
0.180000
0.190000
0.200000
0.210000
0.220000
0.230000
0.240000
0.250000
0.260000
0.270000
0.280000
0.290000
0.300000
0.310000
0.320000
0.330000
0.340000
0.350000
0.360000
0.370000
0.380000
0.390000
0.400000
0.410000
0.420000
0.430000
0.440000
0.450000
0.460000
0.470000
0.480000
0.490000
0.500000
0.510000
0.520000
0.530000
0.540000
0.550000
0.560000
0.570000
0.580000
0.590000
0.600000
0.610000
0.620000
0.630000
0.640000
0.650000
0.660000
0.670000
0.680000
0.690000
0.700000
0.710000
0.720000
0.730000
0.740000
0.750000
0.760000
0.770000
0.780000
0.790000
0.800000
0.810000
0.820000
0.830000
0.839999
0.849999
0.859999
0.869999
0.879999
0.889999
0.899999
0.909999
0.919999
0.929999
0.939999
0.949999
0.959999
0.969999
0.979999
0.989999
0.999999

Como pode ver depois de 0, 83, a precisão diminui significativamente.

No entanto, se eu configurar t como duplo, tal problema não vai acontecer. Levei cinco horas a perceber este pequeno erro, que arruinou o meu programa.
 7
Author: Elliscope Fang, 2018-03-10 11:06:08

Ao usar números de vírgula flutuante, não pode confiar que os seus testes locais serão exactamente os mesmos que os testes que são feitos do lado do servidor. O ambiente e o compilador são provavelmente diferentes em você Sistema local e onde os testes finais são executados. Já vi este problema muitas vezes em algumas competições de Topcoders, especialmente se você tentar comparar dois números de ponto flutuante.

 3
Author: Tuomas Pelkonen, 2010-03-05 13:00:57

As operações de comparação incorporadas diferem como quando se compara 2 números com vírgula flutuante, a diferença no tipo de dados (isto é, vírgula flutuante ou duplo) pode resultar em resultados diferentes.

 2
Author: Johnathan Lau, 2012-11-05 01:35:45

Ao contrário de um int (Número Inteiro), um float tem um ponto decimal, e assim pode um double. Mas a diferença entre os dois é que um double é duas vezes mais detalhado que um float, o que significa que pode ter o dobro da quantidade de números após o ponto decimal.

 -1
Author: Nykal, 2017-09-05 12:10:09