2012-06-13 10 views
10

Me he dado cuenta de que hay mucha discusión sobre el tema de los errores de cálculo de punto flotante que requieren el uso de una comparación más compleja que ==. Sin embargo, todos esos artículos parecen estar asumiendo que el valor es manipulado (o doblemente calculado) de alguna manera, mientras que no vi un ejemplo que cubriera una copia constante muy simple.¿Es seguro el copiado + comparación 'const'?

favor considere lo siguiente:

const double magical_value = -10; 

class Test 
{ 
    double _val; 

public: 
    Test() 
     : _val(magical_value) 
    { 
    } 

    bool is_special() 
    { 
     return _val == magical_value; 
    } 
}; 

Por lo que yo entiendo esto, magical_value se debe establecer en tiempo de compilación, de modo que todo el redondeo se produce en ese punto. Después, el valor solo debería copiarse en la clase y compararse con el original. ¿Se garantiza la comparación como segura? ¿O puede copiar o comparar introducir errores aquí?

Por favor, no sugiera métodos alternativos de comparación o de valor mágico, ese es otro tema. Solo tengo curiosidad sobre esta suposición.

Edición: sólo para nota, soy un poco de miedo de que en algunas arquitecturas, las optimizaciones podría dar lugar a copiar el valor a un diferente tamaño registros de coma flotante, por lo tanto la introducción de las diferencias en los valores exactos. ¿Existe el riesgo de algo así?

+0

¿Se cambiará el valor de '_val' durante el tiempo de ejecución? –

+0

Sí, se puede cambiar a un valor real (pero definitivamente '> -1'). No estoy seguro si habrá una necesidad de 'reiniciarlo' de nuevo al especial. –

+1

Prefiero esperar que copiar un valor de punto flotante no lo altere en absoluto, y que dadas dos variables 'a' y' b' del mismo tipo primitivo, asignando 'a = b' resulta en' a == b' siendo cierto Sin embargo, estoy basando esta suposición en el sentido común, que se sabe que me decepcionó en el pasado. – Rook

Respuesta

2

Es una comparación garantizada para ser seguro? ¿O puede copiar o comparar introducir errores aquí?

Sí, seguro (este es un requisito de la operación de copia implícita en =). No hay conversiones/promociones de las que deba preocuparse, siempre que los tipos de origen y destino sean los mismos.

Sin embargo, tenga en cuenta que magical_value no puede contener 10 exactamente sino una aproximación. Esta aproximación se copiará a _val.

Dado el calificador const, lo más probable es que magical_value probablemente se optimizará distancia (en caso de que encienda optimizaciones) o utilizarse tal como está (es decir, sin memoria, probablemente se agota).

+3

Dudo mucho que exista una implementación donde '10' o' -10' no sean exactamente representables como 'double'. – Henrik

+1

¿No existe el riesgo de que en algunas arquitecturas el valor se pueda copiar en registros de precisión diferente introduciendo diferencias de potencial? –

+0

@Henrik: No se trata del valor 10. Además, muchas personas tienden a ignorar el hecho de que los tipos de coma flotante usan una representación basada en el exponente de mantisa. – dirkgently

0

Aparte de posiblemente de diferentes tamaños registros, has desnormalizado de punto flotante (CQ ras-a-cero) que preocuparse (ver Why does changing 0.1f to 0 slow down performance by 10x?)

Sólo para dar una idea de lo extraño que esto podría conducir a, intenta este bit de código:

float  a = 0.000000000000000000000000000000000000000047683384; 
const float b = 0.000000000000000000000000000000000000000047683384; 
float aa = a, bb = b; 

#define SUPPORT_DENORMALIZATION ({volatile double t=DBL_MIN/2.0;t!=0.0;}) 

printf("support denormals: %d\n",SUPPORT_DENORMALIZATION); 
printf("a = %.48f, aa = %.48f\na==aa %d, a==0.0f %d, aa==0.0f %d\n",a,aa,a==aa,a==0.0f,aa==0.0f); 
printf("b = %.48f, bb = %.48f\nb==bb %d, b==0.0f %d, bb==0.0f %d\n",b,bb,b==bb,b==0.0f,bb==0.0f); 

que da o bien: (compilado sin rubor a cero)

support denormals: 1 
a = 0.000000000000000000000000000000000000000047683384, aa = 0.000000000000000000000000000000000000000047683384 
a==aa 1, a==0.0f 0, aa==0.0f 0 
b = 0.000000000000000000000000000000000000000047683384, bb = 0.000000000000000000000000000000000000000047683384 
b==bb 1, b==0.0f 0, bb==0.0f 0 

o: (compilado con gcc -ffast-math)

support denormals: 0 
a = 0.000000000000000000000000000000000000000000000000, aa = 0.000000000000000000000000000000000000000000000000 
a==aa 1, a==0.0f 1, aa==0.0f 1 
b = 0.000000000000000000000000000000000000000047683384, bb = 0.000000000000000000000000000000000000000000000000 
b==bb 1, b==0.0f 0, bb==0.0f 1 

Cuando la última línea es, por supuesto, el impar hacia fuera: b==bb && b!=0.0f && bb==0.0f sería cierto.

Así que si todavía estás pensando en comparar los valores de punto flotante, al menos mantente alejado de los valores pequeños.

actualización para compensar algunos comentarios acerca de este ser debido al uso de flotadores en lugar de dobles, que también trabaja para el doble, pero se necesitaría establecer la constante a algún lugar por debajo DBL_MIN, por ejemplo, 1e-309.

actualización 2 un ejemplo de código relacionado con algunos comentarios realizados a continuación. Esto demuestra que el problema existe en dobles, así, y que las comparaciones puede llegar a ser inconsistentes (cuando ras a cero está habilitado)

double a; 
    const double b = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001225; 
    const double c = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002225; 

    printf("b==c %d\n",b==c); 
    a = b; 
    printf("assigned a=b: a==b %d\n",a==b); 
    a = c; 
    printf("assigned a=c: a==b %d\n",a==b); 

de salida:

b==c 0 
assigned a=b: a==b 1 
assigned a=c: a==b 1 

La cuestión muestra en la última línea, donde ingenuamente esperaría que a==b se convirtiera en falso después de asignar a=c con c!=b.

+0

-1. Ver mi comentario en mi respuesta. Ya he señalado suficientes problemas con este fragmento de código. Realmente no veo el punto que intentas probar. – dirkgently

+0

@dirkgently amigo, relájate. El punto es: la idea del OP de que el punto flotante puede tener algunas "características" ocultas no está fuera de lugar. – mvds

+0

No veo cómo esto es un problema. En todas sus pruebas, 'a == aa' y' b == bb' eran verdaderas, entonces lo que el OP está haciendo es seguro. Mostraste algunas incoherencias al comparar con cero, pero eso es un problema aparte de lo que está haciendo el PO. – interjay

Cuestiones relacionadas