Representación de trampa
¿Qué es una "representación trampa" en C (algunos ejemplos pueden ayudar)? ¿Esto se aplica a C++?
Dado este código...
float f=3.5; int *pi = (int*)&f;
... y suponiendo que
sizeof(int) == sizeof(float)
, ¿tienenf
y*pi
tienen la misma representación/patrón binario?
Una representación de trampa es un término general utilizado por C99 (IIRC, no por C89) para describir patrones de bits que encajan en el espacio ocupado por un tipo, pero que desencadenan un comportamiento indefinido si se usan como un valor de ese tipo. La definición está en la sección 6.2.6.1p5 (con tentáculos en todo el 6.2.6) y no la voy a citar aquí porque es larga y confusa. Se dice que un tipo para el cual existen tales patrones de bits "tiene" representaciones de trampa. No se requiere que ningún tipo tenga representaciones trampa, pero el único tipo que el estándar garantiza que no tendrá representaciones trampa es
unsigned char
(6.2.6.1p5, 6.2.6.2p1).El estándar ofrece dos ejemplos hipotéticos de representaciones de trampas, ninguno de los cuales corresponde a nada que una CPU real haya hecho durante muchos años, así que no voy a confundirte con ellos. Un buen ejemplo de representación de trampa (también lo único que califica como representación de trampa a nivel de hardware en cualquier CPU que probablemente encuentre) es una señalización NaN en un tipo de punto flotante. El Anexo F del C99 (sección 2.1) deja explícitamente indefinido el comportamiento de los NaN de señalización, aunque IEC 60559 especifica su comportamiento en detalle.
Vale la pena mencionar que, si bien los tipos de puntero pueden tener representaciones de trampa, los punteros nulos no son representaciones de trampa. Los punteros nulos solo causan un comportamiento indefinido si se les desreferencia o se compensan; otras operaciones sobre ellos (lo más importante, comparaciones y copias) están bien definidas. Las representaciones de trampa causan un comportamiento indefinido si simplemente las lee usando el tipo que tiene la representación de trampa. (Es un tema de debate si los punteros no válidos pero no nulos se consideran o deberían considerarse representaciones trampa. La CPU no los trata de esa manera, pero el compilador sí podría hacerlo).
El código que muestra tiene un comportamiento indefinido, pero esto se debe a las reglas de alias de puntero, no a las representaciones de trampa. Así es como convertir a
float
enint
con la misma representación (asumiendo, como usted dice,sizeof(float) == sizeof(int)
)int extract_int(float f) { union { int i; float f; } u; u.f = f; return u.i; }
Este código tiene un comportamiento no especificado (no indefinido) en C99, lo que básicamente significa que el estándar no define qué valor entero se produce, pero sí obtiene algún valor entero válido, no es una representación trampa y el compilador no puede optimizar. suponiendo que no lo haya hecho. (Sección 6.2.6.1, párrafo 7. Mi copia del C99 podría incluir una corrección técnica; lo que recuerdo es que esto no estaba definido en la publicación original, pero se cambió a no especificado en un TC).
Comportamiento indefinido para asignar un alias a un flotante con un puntero a int.
En general, cualquier valor de coma flotante IEEE-754 que no sea trampa se puede representar como un número entero en algunas plataformas sin ningún problema. Sin embargo, hay valores de punto flotante que pueden provocar un comportamiento inesperado si se supone que todos los valores de punto flotante tienen una representación entera única y se fuerza a la FPU a cargar ese valor.
(El ejemplo está tomado de Tipos de coma flotante de intercambio de bytes ).
Por ejemplo, cuando trabaja con datos de punto flotante y necesita agrupar CPU con diferente endianidad, podría pensar en hacer lo siguiente:
double swap(double)
Desafortunadamente, si el compilador carga la entrada en un registro FPU y es una representación trampa, la FPU puede volver a escribirla con una representación trampa equivalente que resulta ser una representación de bits diferente.
En otras palabras, hay algunos valores de coma flotante que no tienen una representación de bits correspondiente si no se convierten correctamente (por correcto me refiero a través de union
, memcpy
vía char *
u otro mecanismo estándar).