una significado reinterpretar reinterpretacion realidad obra interpretar interpretacion filosofia definicion arte c++ c++11 type-conversion language-lawyer standards-compliance

c++ - reinterpretar - interpretar la realidad significado



La forma más eficiente y eficiente de reinterpretar int como float (4)

Afaik, solo hay dos enfoques que cumplen con las reglas estrictas de aliasing: memcpy() y cast to char* con copia. Todos los demás leen un float de la memoria que pertenece a uint32_t , y el compilador puede leer antes de escribir en esa ubicación de memoria. Incluso podría optimizar la escritura por completo, ya que puede demostrar que el valor almacenado nunca se utilizará de acuerdo con reglas de aliasing estrictas, lo que resulta en un valor de retorno de basura.

Realmente depende del compilador / optimiza si la memcpy() o char* es más rápida. En ambos casos, un compilador inteligente podría darse cuenta de que solo puede cargar y copiar un uint32_t , pero no confiaría en ningún compilador antes de haberlo visto en el código ensamblador resultante.

Editar:
Después de algunas pruebas con gcc 4.8.1, puedo decir que el enfoque de memcpy() es el mejor para este compilador de particulare, ver más abajo para más detalles.

Compilando

#include <stdint.h> float foo(uint32_t a) { float b; char* aPointer = (char*)&a, *bPointer = (char*)&b; for( int i = sizeof(a); i--; ) bPointer[i] = aPointer[i]; return b; }

con gcc -S -std=gnu11 -O3 foo.c produce este código de ensamblaje:

movl %edi, %ecx movl %edi, %edx movl %edi, %eax shrl $24, %ecx shrl $16, %edx shrw $8, %ax movb %cl, -1(%rsp) movb %dl, -2(%rsp) movb %al, -3(%rsp) movb %dil, -4(%rsp) movss -4(%rsp), %xmm0 ret

Esto no es óptimo

Haciendo lo mismo con

#include <stdint.h> #include <string.h> float foo(uint32_t a) { float b; char* aPointer = (char*)&a, *bPointer = (char*)&b; memcpy(bPointer, aPointer, sizeof(a)); return b; }

rendimientos (con todos los niveles de optimización excepto -O0 ):

movl %edi, -4(%rsp) movss -4(%rsp), %xmm0 ret

Esto es óptimo.

Supongo que tengo garantías de que float es IEEE 754 binary32. Dado un patrón de bits que corresponde a un float válido, almacenado en std::uint32_t , ¿cómo se lo reinterpreta como un float de la manera más eficiente y compatible?

float reinterpret_as_float(std::uint32_t ui) { return /* apply sorcery to ui */; }

Tengo algunas maneras que sé / sospecho / supongo que tienen algunos problemas:

  1. A través de reinterpret_cast ,

    float reinterpret_as_float(std::uint32_t ui) { return reinterpret_cast<float&>(ui); }

    o equivalente

    float reinterpret_as_float(std::uint32_t ui) { return *reinterpret_cast<float*>(&ui); }

    que sufre problemas de aliasing.

  2. A través de la union ,

    float reinterpret_as_float(std::uint32_t ui) { union { std::uint32_t ui; float f; } u = {ui}; return u.f; }

    que en realidad no es legal, ya que solo está permitido leer desde el más reciente escrito para el miembro. Sin embargo, parece que algunos compiladores (gcc) lo permiten.

  3. Via std::memcpy ,

    float reinterpret_as_float(std::uint32_t ui) { float f; std::memcpy(&f, &ui, 4); return f; }

    que AFAIK es legal, pero una llamada a función para copiar una sola palabra parece un desperdicio, aunque podría optimizarse.

  4. Via reinterpret_cast a char* y copiado,

    float reinterpret_as_float(std::uint32_t ui) { char* uip = reinterpret_cast<char*>(&ui); float f; char* fp = reinterpret_cast<char*>(&f); for (int i = 0; i < 4; ++i) { fp[i] = uip[i]; } return f; }

    que AFAIK también es legal, ya que los punteros de char están exentos de problemas de aliasing y el bucle de copia de byte manual guarda una posible llamada de función. El loop definitivamente se desenrollará, sin embargo, 4 cargas / tiendas de un byte posiblemente son preocupantes, no tengo idea si esto es optimizable para una carga / almacenamiento de solo cuatro bytes.

El 4 es lo mejor que he podido inventar.

¿Estoy correcto hasta ahora? ¿Hay una mejor manera de hacerlo, en particular una que garantice una sola carga / tienda?


Si el patrón de bits en la variable entera es lo mismo que un valor float válido, entonces la unión es probablemente la mejor y más flexible forma de proceder. Y en realidad es legal si lee la especificación (no recuerdo la sección en este momento).



float reinterpret_as_float(std::uint32_t ui) { return *((float *)&ui); }

Como función simple, su código se traduce en ensamblaje como este (Pelles C para Windows):

fld [esp+4] ret

Si se define como función en inline , entonces un código como este ( n es unsigned, x es flotante):

x = reinterpret_as_float (n);

Se traduce al ensamblador como esto:

fld [ebp-4] ;RHS of asignment. Read n as float fstp dword ptr [ebp-8] ;LHS of asignment