Para el bucle en C++ utilizando el doble de romper un paso temprano, el valor límite no alcanzado

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

  •  18-09-2019
  •  | 
  •  

Pregunta

Tengo un simple programa de C++ compilado usando gcc 4.2.4 de 32 bits de Ubuntu 8.04.Tiene un for-bucle en el que un double la variable se incrementa de cero a uno, con un determinado tamaño de paso.Cuando el tamaño de paso es 0.1, el comportamiento es lo que yo esperaba.Pero cuando el tamaño de paso es '0.05', el bucle termina después de 0.95.¿Alguien puede decirme por qué está sucediendo esto?La salida sigue el código fuente a continuación.

#include <iostream>

using namespace std;

int main()
{
    double rangeMin = 0.0;
    double rangeMax = 1.0;
    double stepSize = 0.1;

    for (double index = rangeMin; index <= rangeMax; index+= stepSize)
    {
        cout << index << endl;
    }
    cout << endl; 

    stepSize = 0.05;
    for (double index = rangeMin; index <= rangeMax; index+= stepSize)
    {
        cout << index << endl;
    }

    return 0;
}

SALIDA

sarva@savija-dev:~/code/scratch$ ./a.out 
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1

0
0.05
0.1
0.15
0.2
0.25
0.3
0.35
0.4
0.45
0.5
0.55
0.6
0.65
0.7
0.75
0.8
0.85
0.9
0.95
sarva@savija-dev:~/code/scratch$
¿Fue útil?

Solución

Cuando se utilizan los valores de punto flotante no que cada valor es exactamente representable, 0.95+0.05 > 1 porque 0.95 no es exactamente representable por una double valor.

Ver lo que Wikipedia tiene que decir acerca de de punto flotante de precisión.

Si usted mira la IEEE de punto flotante converter vas a ver que el valor de 0.95 en la versión de 64 bits de punto flotante (double) es 0-01111111110-1110011001100110011001100110011001100110011001100110 al entrar a este en un de punto flotante de la calculadora se obtiene el valor es 0.95000016 y la adición de 0.05 para que te lleva a través de la 1.0 marca.

Esto es por qué usted nunca debe utilizar flotante puntos en bucles (o, más en general comparar el resultado de punto flotante de cálculo para un exacto valor).

Otros consejos

En general, cuando se compara dobles, una simple comparación no es lo suficientemente bueno, y debe compararlos "hasta una precisión". es decir:

if ( fabs(double1-double2) < 0.0000001 ) {
  do-something
}

Se produce el problema debido a la representación de la de variables dobles .

Como han mencionado otros, este es un tema muy conocido debido a la representación inexacta de ciertos números decimales en la memoria. Yo recomendaría la lectura Lo que todo informático debe saber sobre la aritmética de punto flotante y IEEE representaciones de punto flotante de bienes números .

No use == o <= en dobles debido a su representación interna. Por último paso que obtendrá 0.95000000000000029. En su lugar se puede utilizar el siguiente código:

stepSize = 0.05;
// stepSize/2 looks like a good delta for most cases
for (double index = rangeMin; index < rangeMax+stepSize/2; index+= stepSize)
{
    cout << index << endl;
}

Para más detalles, consulte Lo que todo informático debe saber sobre la aritmética de punto flotante .

La mayoría de los decimales exactos no tienen una representación finita exacta en aritmética de punto flotante.

Es necesario leer de Goldberg Lo que todo científico debe saber sobre el ordenador de punto flotante aritmética.

Probablemente el último valor index sería como 1.00000001.

Como otros han dicho, no todos los números reales es exactamente representable como un valor de punto flotante, por lo que puede esperar un pequeño error "al azar" redondeo en los cálculos de punto flotante. Es similar a lo que ocurre con los dígitos decimales normales: 1/3 no es exactamente representable mediante tres cifras decimales (0,33), por lo que (1/3) * 3 se convertiría en 0,99 y no exactamente 1

.

Es posible utilizar algún tipo de "precisión" en sus comparaciones, pero yo recomendaría evitando los números en coma flotante para bucles, y en lugar de utilizar números enteros.

Por ejemplo, el bucle

stepSize = 0.05;
for (double index = rangeMin; index <= rangeMax; index+= stepSize)
{
    cout << index << endl;
}

podría ser reemplazado por algo en la línea de

stepSize = 0.05;
for (int index = 0; index < 21; ++index)
{
    double value = rangeMin + index * stepSize;
    cout << value << endl;
}

Esto es debido a la representación inexacta de fracciones decimales por números de punto flotante. El tamaño de su paso no es en realidad 0,1 o 0,05, que es algún otro valor que está muy cerca. El pequeño error se acumula a medida que avanza a través del bucle.

Para resolver este problema, hay que evitar la comparación de números de punto flotante por la igualdad.

Vea esta salida: (punto flotante de precisión)

#include <iostream>
#include <iomanip>
using namespace std;
int main(){
    double rangeMin = 0.0;
    double rangeMax = 1.0;
    double stepSize = 0.1;
    double index;
    for (index = rangeMin;  index <= rangeMax; index+=stepSize)
        {
               cout << fixed << setprecision(16) <<  index << endl;
         }
  cout << endl;
  stepSize = 0.05;
  for (index = rangeMin; index<= rangeMax; index+= stepSize)
     {
         cout << index << endl;
             }

   cout << "\n" << setprecision(16) << index << " "  << rangeMax;
   if(index==rangeMax)
      cout << "\nEQ";
   else
     cout << "\nNot EQ";
     return 0;
}

0.0000000000000000
0.1000000000000000
0.2000000000000000
0.3000000000000000
0.4000000000000000
0.5000000000000000
0.6000000000000000
0.7000000000000000
0.7999999999999999
0.8999999999999999
0.9999999999999999

0.0000000000000000
0.0500000000000000
0.1000000000000000
0.1500000000000000
0.2000000000000000
0.2500000000000000
0.3000000000000000
0.3500000000000000
0.4000000000000000
0.4500000000000000
0.4999999999999999
0.5499999999999999
0.6000000000000000
0.6500000000000000
0.7000000000000001
0.7500000000000001
0.8000000000000002
0.8500000000000002
0.9000000000000002
0.9500000000000003

1.0000000000000002 1.0000000000000000
Not EQ

A medida que las respuestas anteriores, no es preciso utilizar no enteros en para-bucles, por lo que sugiero que ver como el siguiente ejemplo por lo que mantener la exactitud de los números enteros y usted puede conseguir los decimales que desee:

#include<iostream>
#include<cmath>
#include<iomanip>
using namespace std; 

int main()
{
for (double y = 1; y!=10; y += 1)
    cout << static_cast<double>(y/10) << endl; 



}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top