¿Cuál es la forma correcta de escribir un testando un flotante en un int y viceversa?

El siguiente código realiza una operación de raíz cuadrada inversa rápida mediante algunos hacks de bits. El algoritmo probablemente fue desarrollado por Silicon Graphics a principios de los 90 y también apareció en Quake 3. más información

Sin embargo, recibo la siguiente advertencia del comstackdor C ++ de GCC : desreferenciar puntero tipo-punteado romperá las reglas de alias estrictos

¿Debo usar static_cast , reinterpret_cast o dynamic_cast en tales situaciones?

 float InverseSquareRoot(float x) { float xhalf = 0.5f*x; int32_t i = *(int32_t*)&x; i = 0x5f3759df - (i>>1); x = *(float*)&i; x = x*(1.5f - xhalf*x*x); return x; } 

    Olvida los moldes. Use memcpy .

     float xhalf = 0.5f*x; uint32_t i; assert(sizeof(x) == sizeof(i)); std::memcpy(&i, &x, sizeof(i)); i = 0x5f375a86 - (i>>1); std::memcpy(&x, &i, sizeof(i)); x = x*(1.5f - xhalf*x*x); return x; 

    El código original intenta inicializar el int32_t accediendo primero al objeto float través de un puntero int32_t , que es donde se rompen las reglas. El elenco estilo C es equivalente a reinterpret_cast , por lo que cambiarlo a reinterpret_cast no supondría mucha diferencia.

    La diferencia importante cuando se usa memcpy es que los bytes se copian del float en int32_t , pero nunca se accede al objeto float a través de int32_t , porque memcpy toma punteros para anular y su interior es “mágico” y no rompe el reglas de aliasing

    Aquí hay algunas buenas respuestas que abordan el problema del tipo de juego de palabras.

    Quiero abordar la parte “raíz inversa cuadrada rápida”. No use este “truco” en procesadores modernos. Todos los vectores principales ISA tiene una instrucción de hardware dedicada para darle una raíz cuadrada inversa rápida. Cada uno de ellos es más rápido y más preciso que este pequeño truco copiado.

    Estas instrucciones están todas disponibles a través de intrínsecos, por lo que son relativamente fáciles de usar. En SSE, quiere usar rsqrtss (intrinsic: _mm_rsqrt_ss( ) ); en NEON quieres usar vrsqrte (intrinsic: vrsqrte_f32( ) ); y en AltiVec quieres usar frsqrte . La mayoría de las ISA de GPU tienen instrucciones similares. Estas estimaciones se pueden refinar usando la misma iteración de Newton, y NEON incluso tiene la instrucción vrsqrts para hacer parte del refinamiento en una instrucción única sin la necesidad de cargar constantes.

    Estoy agregando una respuesta para no refutar la respuesta aceptada, sino para boostla. Creo que la respuesta aceptada es correcta y eficiente (y acabo de subirla). Sin embargo, quería demostrar otra técnica que es igual de correcta y eficiente:

     float InverseSquareRoot(float x) { union { float as_float; int32_t as_int; }; float xhalf = 0.5f*x; as_float = x; as_int = 0x5f3759df - (as_int>>1); as_float = as_float*(1.5f - xhalf*as_float*as_float); return as_float; } 

    Usando clang ++ con optimización en -O3, compilé el código de plasmacel, el código de R. Martinho Fernandes y este código, y comparé el ensamblaje línea por línea. Los tres fueron idénticos.

    Actualizar

    Ya no creo que esta respuesta sea correcta, debido a los comentarios que recibí del comité. Pero quiero dejarlo para propósitos informativos. Y tengo la firme esperanza de que el comité pueda hacer esta respuesta correcta (si así lo desea). Es decir, no hay nada sobre el hardware subyacente que haga que esta respuesta sea incorrecta, es solo el juicio de un comité lo que hace que sea así, o no.

    El único elenco que funcionará aquí es reinterpret_cast . (Y aun así, al menos un comstackdor saldrá de su camino para asegurarse de que no funcione).

    ¿Pero qué estás tratando de hacer realmente? Ciertamente hay una solución mejor, que no involucra el tipo de juego de palabras. Hay muy, muy pocos casos en que el tipo de juego de palabras sea apropiado, y todos están en un código de muy, muy bajo nivel, cosas como la serialización o la implementación de la biblioteca estándar C (por ejemplo, funciones como modf ). De lo contrario (y tal vez incluso en serialización), funciones como ldexp y modf probablemente funcionen mejor, y sin duda serán más legibles.

    Eche un vistazo a esto para obtener más información sobre el tipo de juego de palabras y el alias estricto.

    El único tipo seguro de un tipo en una matriz es en una matriz de caracteres. Si desea que una dirección de datos se pueda cambiar a diferentes tipos, necesitará usar un union

    El lanzamiento invoca un comportamiento indefinido. No importa qué tipo de elenco utilices, seguirá siendo un comportamiento indefinido. No está definido, independientemente del tipo de yeso que use.

    La mayoría de los comstackdores harán lo que esperas, pero a gcc le gusta ser malo y probablemente asumirá que no asignaste los apuntadores a pesar de todas las indicaciones que hiciste y reordenar la operación, por lo que dan un resultado extraño.

    Lanzar un puntero al tipo incompatible y desreferenciarlo es un comportamiento indefinido. La única excepción es std::memcpy desde o hacia char, por lo que la única solución es usar std::memcpy (según la respuesta de R. Martinho Fernandes). (No estoy seguro de cuánto se define al usar sindicatos, pero tiene más posibilidades de funcionar).

    Dicho esto, no deberías usar el molde C-style en C ++. En este caso, static_cast no comstackría, ni tampoco dynamic_cast , lo que le obligaría a usar reinterpret_cast y reinterpret_cast es una fuerte sugerencia de que podría estar violando reglas de alias estrictas.

    Basado en las respuestas aquí hice una función moderna de “pseudo-cast” para facilitar la aplicación.

    Versión C99 (aunque la mayoría de los comstackdores lo admiten, teóricamente podría ser un comportamiento indefinido en algunos)

     template  inline T pseudo_cast(const U &x) { static_assert(std::is_trivially_copyable::value && std::is_trivially_copyable::value, "pseudo_cast can't handle types which are not trivially copyable"); union { U from; T to; } __x = {x}; return __x.to; } 

    Versiones universales (basadas en la respuesta aceptada)

    Cast tipos con el mismo tamaño:

     #include  template  inline T pseudo_cast(const U &x) { static_assert(std::is_trivially_copyable::value && std::is_trivially_copyable::value, "pseudo_cast can't handle types which are not trivially copyable"); static_assert(sizeof(T) == sizeof(U), "pseudo_cast can't handle types with different size"); T to; std::memcpy(&to, &x, sizeof(T)); return to; } 

    Cast tipos con cualquier tamaño:

     #include  template  inline T pseudo_cast(const U &x) { static_assert(std::is_trivially_copyable::value && std::is_trivially_copyable::value, "pseudo_cast can't handle types which are not trivially copyable"); T to = T(0); std::memcpy(&to, &x, (sizeof(T) < sizeof(U)) ? sizeof(T) : sizeof(U)); return to; } 

    Úselo como:

     float f = 3.14f; uint32_t u = pseudo_cast(f);