¿Cuál es la diferencia entre float y double?


420

He leído sobre la diferencia entre precisión doble y precisión simple. Sin embargo, en la mayoría de los casos, floaty doubleparecen ser intercambiables, es decir, usar uno u otro no parece afectar los resultados. ¿Es este realmente el caso? ¿Cuándo son intercambiables los flotadores y los dobles? Cuáles son las diferencias entre ellos?

Respuestas:


521

Gran diferencia.

Como su nombre lo indica, a doubletiene 2 veces la precisión de [1] . En general, a tiene 15 dígitos decimales de precisión, mientras que tiene 7.floatdoublefloat

Así se calcula el número de dígitos:

doubletiene 52 bits de mantisa + 1 bit oculto: log (2 53 ) ÷ log (10) = 15.95 dígitos

floattiene 23 bits de mantisa + 1 bit oculto: log (2 24 ) ÷ log (10) = 7.22 dígitos

Esta pérdida de precisión podría conducir a la acumulación de errores de truncamiento mayores cuando se realizan cálculos repetidos, por ej.

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

mientras

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

Además, el valor máximo de flotante es aproximadamente 3e38, pero el doble es aproximadamente 1.7e308, por lo que usar floatpuede alcanzar el "infinito" (es decir, un número especial de coma flotante) mucho más fácilmente que doublepara algo simple, por ejemplo, calcular el factorial de 60.

Durante las pruebas, tal vez algunos casos de prueba contengan estos números enormes, lo que puede hacer que sus programas fallen si usa flotadores.


Por supuesto, a veces, incluso doubleno es lo suficientemente preciso, por lo tanto, a veces tenemos long double[1] (el ejemplo anterior da 9.000000000000000066 en Mac), pero todos los tipos de coma flotante sufren errores de redondeo , por lo que si la precisión es muy importante (por ejemplo, dinero procesamiento) debe usar into una clase de fracción.


Además, no utilice +=para sumar muchos números de coma flotante, ya que los errores se acumulan rápidamente. Si estás usando Python, úsalo fsum. De lo contrario, intente implementar el algoritmo de suma Kahan .


[1]: Los estándares C y C ++ no especifican la representación de float, doubley long double. Es posible que los tres se implementen como IEEE de doble precisión. Sin embargo, para la mayoría de las arquitecturas (gcc, MSVC; x86, x64, ARM) float es de hecho un número de coma flotante de precisión simple IEEE (binary32), y double es un número de coma flotante de precisión doble IEEE (binary64).


99
El consejo habitual para la suma es ordenar los números de coma flotante por magnitud (el más pequeño primero) antes de sumar.
R .. GitHub DEJA DE AYUDAR A HIELO

Tenga en cuenta que si bien C / C ++ float y double son casi siempre IEEE simple y doble precisión, respectivamente, C / C ++ long double es mucho más variable dependiendo de su CPU, compilador y sistema operativo. A veces es lo mismo que doble, a veces es un formato extendido específico del sistema, a veces es precisión cuádruple IEEE.
lavar

@ R..GitHubSTOPHELPINGICE: ¿por qué? ¿Podrías explicar?
InQusitive

@InQusitive: considere, por ejemplo, una matriz que consiste en el valor 2 ^ 24 seguido de 2 ^ 24 repeticiones del valor 1. Sumar en orden produce 2 ^ 24. La inversión produce 2 ^ 25. Por supuesto, puede hacer ejemplos (p. Ej., Hacer 2 ^ 25 repeticiones de 1) donde cualquier orden termina siendo catastróficamente incorrecta con un solo acumulador, pero la de menor magnitud es la mejor de todas. Para hacerlo mejor necesitas algún tipo de árbol.
R .. GitHub DEJA DE AYUDAR A ICE

56

Esto es lo que dicen las normas estándar C99 (ISO-IEC 9899 6.2.5 §10) o C ++ 2003 (ISO-IEC 14882-2003 3.1.9 §8):

Hay tres tipos de coma flotante: float, double, y long double. El tipo doubleproporciona al menos tanta precisión como float, y el tipo long doubleproporciona al menos tanta precisión como double. El conjunto de valores del tipo floates un subconjunto del conjunto de valores del tipo double; El conjunto de valores del tipo doublees un subconjunto del conjunto de valores del tipo long double.

El estándar C ++ agrega:

La representación del valor de los tipos de punto flotante está definida por la implementación.

Sugeriría echar un vistazo a lo excelente que todo informático debe saber sobre la aritmética de punto flotante que cubre en profundidad el estándar de punto flotante IEEE. Aprenderá sobre los detalles de la representación y se dará cuenta de que existe una compensación entre magnitud y precisión. La precisión de la representación de coma flotante aumenta a medida que disminuye la magnitud, por lo tanto, los números de coma flotante entre -1 y 1 son los que tienen más precisión.


27

Dada una ecuación cuadrática: x 2  - 4.0000000  x  + 3.9999999 = 0, las raíces exactas a 10 dígitos significativos son, r 1  = 2.000316228 y r 2  = 1.999683772.

Usando floaty double, podemos escribir un programa de prueba:

#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;
}  

Ejecutar el programa me da:

2.00000 2.00000
2.00032 1.99968

Tenga en cuenta que los números no son grandes, pero aún así obtiene efectos de cancelación utilizando float.

(De hecho, lo anterior no es la mejor manera de resolver ecuaciones cuadráticas utilizando números de coma flotante de precisión simple o doble, pero la respuesta permanece sin cambios incluso si se usa un método más estable ).


19
  • Un doble es 64 y la precisión simple (flotante) es de 32 bits.
  • El doble tiene una mantisa más grande (los bits enteros del número real).
  • Cualquier imprecisión será menor en el doble.

12

El tamaño de los números involucrados en los cálculos de coma flotante no es lo más relevante. Es el cálculo que se realiza lo que es relevante.

En esencia, si está realizando un cálculo y el resultado es un número irracional o un decimal recurrente, entonces habrá errores de redondeo cuando ese número se aplaste en la estructura de datos de tamaño finito que está utilizando. Como el doble es dos veces el tamaño del flotador, el error de redondeo será mucho menor.

Las pruebas pueden usar específicamente números que causarían este tipo de error y, por lo tanto, probaron que usaste el tipo apropiado en tu código.


9

El tipo flotante, de 32 bits de largo, tiene una precisión de 7 dígitos. Si bien puede almacenar valores con un rango muy grande o muy pequeño (+/- 3.4 * 10 ^ 38 o * 10 ^ -38), solo tiene 7 dígitos significativos.

Tipo doble, 64 bits de largo, tiene un rango mayor (* 10 ^ + / - 308) y precisión de 15 dígitos.

El tipo long double es nominalmente 80 bits, aunque un compilador / sistema operativo determinado puede almacenarlo como 12-16 bytes para fines de alineación. El doble largo tiene un exponente que es ridículamente enorme y debe tener una precisión de 19 dígitos. Microsoft, en su sabiduría infinita, limita el doble largo a 8 bytes, lo mismo que el doble simple.

En términos generales, solo use type double cuando necesite un valor / variable de coma flotante. Los valores literales de coma flotante utilizados en las expresiones se tratarán como dobles por defecto, y la mayoría de las funciones matemáticas que devuelven valores de coma flotante devuelven dobles. Se ahorrará muchos dolores de cabeza y encasillados si solo usa el doble.



9

Me topé con un error que me llevó una eternidad descubrir y potencialmente puede darle un buen ejemplo de precisión de flotación.

#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;
  }
}

La salida es

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 puede ver después de 0.83, la precisión se reduce significativamente.

Sin embargo, si configuro tcomo doble, tal problema no sucederá.

Me tomó cinco horas darme cuenta de este pequeño error, que arruinó mi programa.


44
solo para estar seguro: ¿la solución de su problema debería ser usar un int preferiblemente? Si desea iterar 100 veces, debe contar con un int en lugar de usar un doble
BlueTrin

8
Usar doubleno es una buena solución aquí. Sueles intcontar y hacer una multiplicación interna para obtener tu valor de coma flotante.
Richard


3

Cuando utilice números de coma flotante, no puede confiar en que sus pruebas locales serán exactamente las mismas que las pruebas que se realizan en el lado del servidor. El entorno y el compilador son probablemente diferentes en su sistema local y donde se ejecutan las pruebas finales. He visto este problema muchas veces antes en algunas competiciones de TopCoder, especialmente si intentas comparar dos números de coma flotante.


3

Las operaciones de comparación incorporadas difieren, ya que cuando compara 2 números con coma flotante, la diferencia en el tipo de datos (es decir, flotante o doble) puede dar lugar a resultados diferentes.


1

Si se trabaja con procesamiento incorporado, eventualmente el hardware subyacente (por ejemplo, FPGA o algún modelo específico de procesador / microcontrolador) se implementará flotantemente de manera óptima en el hardware, mientras que el doble utilizará rutinas de software. Entonces, si la precisión de un flotante es suficiente para manejar las necesidades, el programa se ejecutará algunas veces más rápido con flotante y luego doble. Como se señaló en otras respuestas, tenga cuidado con los errores de acumulación.


-1

A diferencia de un int(número entero), a floattiene un punto decimal, y también puede a double. Pero la diferencia entre los dos es que a doublees dos veces más detallado que a float, lo que significa que puede tener el doble de números después del punto decimal.


44
No significa eso en absoluto. En realidad, significa el doble de dígitos decimales integrales , y es más del doble. La relación entre los dígitos fraccionarios y la precisión no es lineal: depende del valor: por ejemplo, 0.5 es preciso pero 0.33333333333333333333 no lo es.
Marqués de Lorne
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.