floating-point - programacion - punto flotante ieee 754 ejemplos
¿Hay un valor de coma flotante de x, para el cual xx== 0 es falso? (6)
En la mayoría de los casos, entiendo que se debe implementar una prueba de comparación de punto flotante usando un rango de valores (abs (xy) <epsilon), pero ¿la auto resta implica que el resultado será cero?
// can the assertion be triggered?
float x = //?;
assert( x-x == 0 )
Supongo que nan / inf pueden ser casos especiales, pero estoy más interesado en lo que sucede con los valores simples.
editar:
¿Me complace elegir una respuesta si alguien puede citar una referencia (estándar de punto flotante IEEE)?
Como insinuó, inf - inf
es NaN
, que no es igual a cero. Del mismo modo, NaN - NaN
es NaN
. Sin embargo, es cierto que para cualquier número finito de coma flotante x
, x - x == 0.0
(dependiendo del modo de redondeo, el resultado de x - x
puede ser un cero negativo, pero un cero negativo se compara con 0.0
en flotante aritmética de puntos).
Edición: es un poco difícil dar una referencia clara a los estándares, porque esta es una propiedad emergente de las reglas establecidas en el estándar IEEE-754. Específicamente, se deriva del requisito de que las operaciones definidas en la Cláusula 5 estén correctamente redondeadas. La resta es una operación de este tipo (Sección 5.4.1 "Operaciones aritméticas"), y el resultado correctamente redondeado de x - x
es un cero del signo apropiado (Sección 6.3, párrafo 3):
Cuando la suma de dos operandos con signos opuestos (o la diferencia de dos operandos con signos semejantes) es exactamente cero, el signo de esa suma (o diferencia) será +0 en todos los atributos de dirección de redondeo excepto roundTowardNegative; bajo ese atributo, el signo de una suma cero exacta (o diferencia) será −0.
Entonces, el resultado de x - x
debe ser +/- 0
, y por lo tanto debe ser igual a 0.0
(Sección 5.11, párrafo 2):
Las comparaciones ignorarán el signo de cero.
Edición adicional: Eso no quiere decir que un compilador de buggy no pueda hacer que esa afirmación se dispare. Tu pregunta es ambigua; no hay un número finito de coma flotante x
tal que x - x == 0
sea falso. Sin embargo, eso no es lo que comprueba el código que ha publicado; verifica si una determinada expresión en un lenguaje de estilo C puede evaluar un valor distinto de cero; en particular, en ciertas plataformas, con ciertas optimizaciones del compilador (mal concebidas), las dos instancias de la variable x
en esa expresión pueden tener valores diferentes , lo que hace que falle la afirmación (especialmente si x
es el resultado de algún cálculo, en lugar de un valor constante, representable). Este es un error en el modelo numérico en esas plataformas, pero eso no significa que no pueda suceder.
Con respecto a lo que dice Mark: echa un vistazo a este enlace http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.18 . (No estoy seguro si se aplica a tu situación).
Mi respuesta a la pregunta principal: "¿Hay un valor de coma flotante de x, para el que xx == 0 es falso?" es: al menos la implementación de punto flotante en los procesadores Intel NO hace que el flujo aritmético en las operaciones "+" y "-", por lo que no podrá encontrar una x para la que xx == 0 sea falso. Lo mismo es cierto para todos los procesadores que admiten IEEE 754-2008 (consulte las referencias a continuación).
Mi respuesta corta en otra pregunta: si (xy == 0) es exactamente tan seguro como si (x == y), entonces aseverar (xx == 0) está bien, porque no se producirá un desbordamiento aritmético en xx o ( xy).
La razón está siguiendo. Un número flotante / doble se mantendrá en la memoria en forma de mantisa y exponente binario. En el caso estándar, la mantisa está normalizada: es> = 0.5 y <1. En <float.h>
puede encontrar algunas constantes del estándar de punto flotante IEEE. Interesante ahora para nosotros solo seguimos
#define DBL_MIN 2.2250738585072014e-308 /* min positive value */
#define DBL_MIN_10_EXP (-307) /* min decimal exponent */
#define DBL_MIN_EXP (-1021) /* min binary exponent */
Pero no todo el mundo sabe, que puede tener números dobles menos que DBL_MIN. Si realiza operaciones aritméticas con números bajo DBL_MIN, este número NO se normalizará, por lo que trabaja con estos números como con números enteros (operación con mantisa solamente) sin ningún "error de redondeo".
Nota : yo personalmente trato de no usar palabras "errores redondos", porque no hay errores en las operaciones aritméticas de la computadora. Estas operaciones no son las mismas que las operaciones +, -, * y / con los mismos números de computadora como número flotante. Hay operaciones deterministas en el subconjunto de números de punto flotante que se pueden guardar en la forma (mantisa, exponente) con un número bien definido de bits para cada uno. Tal subconjunto de flotadores podemos nombrar como número flotante de la computadora . Por lo tanto, el resultado de la operación de punto flotante clásico se proyectará nuevamente al conjunto de números flotantes de la computadora. Dicha operación de proyección es determinista, y tiene muchas características como si x1> = x2 luego x1 * y> = x2 * y.
Lo siento por el largo comentario y volvemos a nuestro tema.
Para mostrar exactamente lo que tenemos si operamos con menos números, entonces DBL_MIN escribí un pequeño programa en C:
#include <stdio.h>
#include <float.h>
#include <math.h>
void DumpDouble(double d)
{
unsigned char *b = (unsigned char *)&d;
int i;
for (i=1; i<=sizeof(d); i++) {
printf ("%02X", b[sizeof(d)-i]);
}
printf ("/n");
}
int main()
{
double x, m, y, z;
int exp;
printf ("DBL_MAX=%.16e/n", DBL_MAX);
printf ("DBL_MAX in binary form: ");
DumpDouble(DBL_MAX);
printf ("DBL_MIN=%.16e/n", DBL_MIN);
printf ("DBL_MIN in binary form: ");
DumpDouble(DBL_MIN);
// Breaks the floating point number x into its binary significand
// (a floating point value between 0.5(included) and 1.0(excluded))
// and an integral exponent for 2
x = DBL_MIN;
m = frexp (x, &exp);
printf ("DBL_MIN has mantissa=%.16e and exponent=%d/n", m, exp);
printf ("mantissa of DBL_MIN in binary form: ");
DumpDouble(m);
// ldexp() returns the resulting floating point value from
// multiplying x (the significand) by 2
// raised to the power of exp (the exponent).
x = ldexp (0.5, DBL_MIN_EXP); // -1021
printf ("the number (x) constructed from mantissa 0.5 and exponent=DBL_MIN_EXP (%d) in binary form: ", DBL_MIN_EXP);
DumpDouble(x);
y = ldexp (0.5000000000000001, DBL_MIN_EXP);
m = frexp (y, &exp);
printf ("the number (y) constructed from mantissa 0.5000000000000001 and exponent=DBL_MIN_EXP (%d) in binary form: ", DBL_MIN_EXP);
DumpDouble(y);
printf ("mantissa of this number saved as double will be displayed by printf(%%.16e) as %.16e and exponent=%d/n", m, exp);
y = ldexp ((1 + DBL_EPSILON)/2, DBL_MIN_EXP);
m = frexp (y, &exp);
printf ("the number (y) constructed from mantissa (1+DBL_EPSILON)/2 and exponent=DBL_MIN_EXP (%d) in binary form: ", DBL_MIN_EXP);
DumpDouble(y);
printf ("mantissa of this number saved as double will be displayed by printf(%%.16e) as %.16e and exponent=%d/n", m, exp);
z = y - x;
m = frexp (z, &exp);
printf ("z=y-x in binary form: ");
DumpDouble(z);
printf ("z will be displayed by printf(%%.16e) as %.16e/n", z);
printf ("z has mantissa=%.16e and exponent=%d/n", m, exp);
if (x == y)
printf ("/"if (x == y)/" say x == y/n");
else
printf ("/"if (x == y)/" say x != y/n");
if ((x-y) == 0)
printf ("/"if ((x-y) == 0)/" say /"(x-y) == 0/"/n");
else
printf ("/"if ((x-y) == 0)/" say /"(x-y) != 0/"/n");
}
Este código produjo el siguiente resultado:
DBL_MAX=1.7976931348623157e+308
DBL_MAX in binary form: 7FEFFFFFFFFFFFFF
DBL_MIN=2.2250738585072014e-308
DBL_MIN in binary form: 0010000000000000
DBL_MIN has mantissa=5.0000000000000000e-001 and exponent=-1021
mantissa of DBL_MIN in binary form: 3FE0000000000000
the number (x) constructed from mantissa 0.5 and exponent=DBL_MIN_EXP (-1021) in binary form: 0010000000000000
the number (y) constructed from mantissa 0.5000000000000001 and exponent=DBL_MIN_EXP (-1021) in binary form: 0010000000000001
mantissa of this number saved as double will be displayed by printf(%.16e) as 5.0000000000000011e-001 and exponent=-1021
the number (y) constructed from mantissa (1+DBL_EPSILON)/2 and exponent=DBL_MIN_EXP (-1021) in binary form: 0010000000000001
mantissa of this number saved as double will be displayed by printf(%.16e) as 5.0000000000000011e-001 and exponent=-1021
z=y-x in binary form: 0000000000000001
z will be displayed by printf(%.16e) as 4.9406564584124654e-324
z has mantissa=5.0000000000000000e-001 and exponent=-1073
"if (x == y)" say x != y
"if ((x-y) == 0)" say "(x-y) != 0"
Así que podemos ver que si trabajamos con menos números que DBL_MIN, no se normalizarán (consulte 0000000000000001
). Estamos trabajando con estos números como con enteros y sin ningún "error". Por lo tanto, si asignamos y=x
entonces if (xy == 0)
es exactamente tan seguro como if (x == y)
, y assert(xx == 0)
funcione correctamente. En este ejemplo, z = 0.5 * 2 ^ (- 1073) = 1 * 2 ^ (- 1072). Este número es realmente el número más pequeño que podemos haber guardado en doble. Toda operación aritmética con números menos DBL_MIN funciona como con un entero multiplicado con 2 ^ (- 1072).
Así que no tengo problemas de subdesbordamiento en mi computadora con Windows 7 con procesador Intel. Si alguien tiene otro procesador sería interesante comparar nuestros resultados .
¿Tiene alguien una idea de cómo se puede producir un desbordamiento aritmético con operaciones - o +? Mis experimentos parecen así, que es imposible.
EDITADO : modifiqué un poco el código para mejorar la legibilidad del código y los mensajes.
ENLACES AÑADIDOS : Mis experimentos muestran que http://grouper.ieee.org/groups/754/faq.html#underflow es absolutamente correcto en mi CPU Intel Core 2. La forma en que se calculará no produce subdesbordamiento en las operaciones de punto flotante "+" y "-". Mis resultados son independientes en los modificadores del compilador Microsoft Visual C estricto (/ fp: estricto) o preciso (/ fp: preciso) (consulte http://msdn.microsoft.com/en-us/library/e7s85ffb%28VS.80%29.aspx y http://msdn.microsoft.com/en-us/library/Aa289157 )
UNO MÁS (PROBABLEMENTE EL ÚLTIMO) VÍNCULO Y MI NOTA FINAL : Encontré una buena referencia http://en.wikipedia.org/wiki/Subnormal_numbers , donde se describe lo mismo que escribí antes. La inclusión de números denormales o números desnormalizados (ahora a menudo llamados números subnormales, por ejemplo, en In IEEE 754-2008 ) sigue a la siguiente declaración:
“Los números denormales ofrecen la garantía de que la suma y la resta de los números de punto flotante nunca se desbordan; dos números de punto flotante cercanos siempre tienen una diferencia no nula representable. Sin un desbordamiento gradual, la resta a − b puede desbordarse y producir cero aunque los valores no sean iguales ”.
Por lo tanto, todos mis resultados deben ser correctos en cualquier procesador que admita IEEE 754-2008.
Sí, aparte de los casos especiales, xx
siempre será 0. Pero x*(1/x)
no siempre será 1 ;-)
Sí, la auto resta siempre debe resultar en cero, excepto en casos especiales.
El problema ocurre cuando sumas, restas, multiplicas o divides antes de una comparación donde se ajustan el exponente y la mantisa. Cuando los exponentes son iguales, las mantisas se restan, y si son iguales, todo termina en cero.
Si la representación se transforma (por ejemplo, de un formato de memoria de 64 bits a un formato de registro interno de 80 bits en x86), esperaría que la afirmación pudiera dispararse en algunas circunstancias.