Recibo un búfer de una red que se convirtió en una matriz de palabras de 32 bits. Tengo una palabra que se define como flotante ieee en mi documento de interfaz. Necesito extraer esta palabra del búfer. Es difícil pasar de un tipo a otro sin invocar una conversión. Los bits ya se adhieren al estándar flotante ieee, no quiero reorganizar ningún bit.

Mi primer intento fue convertir la dirección de uint32_t a void*, luego convertir void* a float*, luego eliminar la referencia como float:

float ieee_float(uint32_t f)
{
    return *((float*)((void*)(&f)));
}

error: desreferenciar el puntero con tipo de juego de palabras romperá las reglas de alias estricto [-Werror = aliasing estricto]

Mi segundo intento fue así:

float ieee_float(uint32_t f)
{
    union int_float{
        uint32_t i;
        float f;
    } tofloat;

    tofloat.i = f;
    return tofloat.f;
}

Sin embargo, se dice en la calle que los sindicatos son totalmente inseguros. Es un comportamiento indefinido leer del miembro del sindicato que no se escribió más recientemente.

Entonces probé un enfoque más C ++:

float ieee_float(uint32_t f)
{
  return *reinterpret_cast<float*>(&f);
}

error: desreferenciar el puntero con tipo de juego de palabras romperá las reglas de alias estricto [-Werror = aliasing estricto]

Mi siguiente pensamiento fue "al diablo. ¿Por qué estoy tratando con punteros de todos modos?" y solo probé:

float ieee_float(uint32_t f)
{
  return reinterpret_cast<float>(f);
}

error: conversión no válida del tipo "uint32_t {también conocido como unsigned int}" para escribir "float"

¿Hay alguna forma de realizar la conversión sin activar la advertencia / error? Estoy compilando con g ++ usando -Wall -Werror. Preferiría no tocar la configuración del compilador.

Etiqueté C porque una solución C es aceptable.

c++
4
Stewart 15 feb. 2018 a las 12:02

2 respuestas

La mejor respuesta

En C ++, la forma correcta ™ es:

float ieee_float(uint32_t f)
{
    static_assert(sizeof(float) == sizeof f, "`float` has a weird size.");
    float ret;
    std::memcpy(&ret, &f, sizeof(float));
    return ret;
}

Tanto GCC como Clang en -O1 y superiores generan el mismo ensamblado para este código y un reinterpret_cast<float &>(f) ingenuo.

8
HolyBlackCat 15 feb. 2018 a las 09:26

Tiene varias opciones, como se indica aquí:

  • Use la solución union: desde C11 está explícitamente permitido (como se dice en la otra respuesta).
  • En lugar de usar una matriz de palabras de 32 bits, use una matriz de palabras de 8 bits (uint8_t), ya que los tipos char pueden tener un alias de cualquier tipo.
1
LoPiTaL 15 feb. 2018 a las 09:11