Saltar al contenido

Conversión de Int a Float o Float a Int usando operaciones Bitwise (punto flotante de software)

Bienvenido a nuestra página web, en este sitio vas a encontrar la resolución a lo que buscas.

Solución:

Primero, un artículo que debería considerar leer, si quiere comprender mejor las debilidades del punto flotante: “Lo que todo científico informático debe saber sobre la aritmética del punto flotante”, http://www.validlab.com/goldberg/paper.pdf

Y ahora un poco de carne.

El siguiente código es básico e intenta producir un flotador de precisión simple IEEE-754 a partir de un unsigned int en el rango 0 24. Ese es el formato que es más probable que encuentre en el hardware moderno, y es el formato al que parece hacer referencia en su pregunta original.

Los flotantes de precisión simple IEEE-754 se dividen en tres campos: un bit de signo único, 8 bits de exponente y 23 bits de significando (a veces llamado mantisa). IEEE-754 usa un oculto 1 significand, lo que significa que el significand es en realidad un total de 24 bits. Los bits se empaquetan de izquierda a derecha, con el bit de signo en el bit 31, el exponente en los bits 30 .. 23 y el significado en los bits 22 .. 0. El siguiente diagrama de Wikipedia ilustra:

formato de punto flotante

El exponente tiene un sesgo de 127, lo que significa que el exponente real asociado con el número de coma flotante es 127 menos que el valor almacenado en el campo del exponente. Por tanto, un exponente de 0 se codificaría como 127.

(Nota: Puede que le interese el artículo completo de Wikipedia. Ref: http://en.wikipedia.org/wiki/Single_precision_floating-point_format)

Por lo tanto, el número IEEE-754 0x40000000 se interpreta de la siguiente manera:

  • Bit 31 = 0: valor positivo
  • Bits 30 .. 23 = 0x80: Exponent = 128-127 = 1 (también conocido como 21)
  • Los bits 22 .. 0 son todos 0: Significand = 1.00000000_00000000_0000000. (Tenga en cuenta que restauré el 1 oculto).

Entonces el valor es 1.0 x 21 = 2,0.

Para convertir un unsigned int en el rango limitado dado anteriormente, entonces, a algo en formato IEEE-754, puede usar una función como la que se muestra a continuación. Toma los siguientes pasos:

  • Alinea el 1 inicial del entero con la posición del oculto 1 en la representación de coma flotante.
  • Al alinear el entero, registra el número total de cambios realizados.
  • Enmascara lo oculto 1.
  • Usando el número de cambios realizados, calcula el exponente y lo agrega al número.
  • Utilizando reinterpret_cast, convierte el patrón de bits resultante en un float. Esta parte es un truco feo, porque usa un puntero con tipo de juego. También puede hacer esto abusando de un union. Algunas plataformas proporcionan una operación intrínseca (como _itof) para hacer esta reinterpretación menos fea.

Hay formas mucho más rápidas de hacer esto; este está destinado a ser pedagógicamente útil, si no súper eficiente:

float uint_to_float(unsigned int significand)
 (significand & 0x7FFFFF);


    //  Reinterpret as a float and return.  This is an evil hack.
    return *reinterpret_cast< float* >( &merged );

Puede hacer que este proceso sea más eficiente utilizando funciones que detectan el 1 inicial en un número. (Estos a veces tienen nombres como clz para “contar ceros a la izquierda”, o norm para “normalizar”.)

También puede extender esto a números con signo registrando el signo, tomando el valor absoluto del entero, realizando los pasos anteriores y luego colocando el signo en el bit 31 del número.

Para enteros> = 224, el entero entero no encaja en el campo significativo del formato flotante de 32 bits. Esta es la razón por la que necesita “redondear”: pierde LSB para que el valor se ajuste. Por lo tanto, varios enteros terminarán mapeando al mismo patrón de punto flotante. El mapeo exacto depende del modo de redondeo (redondear hacia -Inf, redondear hacia + Inf, redondear hacia cero, redondear hacia el par más cercano). Pero el quid de la cuestión es que no puede introducir 24 bits en menos de 24 bits sin alguna pérdida.

Puede ver esto en términos del código anterior. Funciona alineando el 1 principal con la posición 1 oculta. Si un valor era> = 224, el código debería cambiar Derecha, no izquierda, y eso necesariamente aleja a los LSB. Los modos de redondeo solo le dicen cómo manejar los bits desplazados.

¿Ha verificado la representación de punto flotante IEEE 754?

En forma normalizada de 32 bits, tiene el bit de signo (mantisa), exponente de 8 bits (exceso-127, creo) y mantisa de 23 bits en “decimal” excepto que el “0”. se elimina (siempre en esa forma) y la base es 2, no 10. Es decir: el valor de MSB es 1/2, el siguiente bit 1/4 y así sucesivamente.

La respuesta de Joe Z es elegante, pero el rango de valores de entrada es muy limitado. El flotador de 32 bits puede almacenar todos los valores enteros del siguiente rango:

[-224…+224] = [-16777216…+16777216]

y algunos otros valores fuera de este rango.

Todo el rango estaría cubierto por esto:

float int2float(int value)

    // handles all values from [-2^24...2^24]
    // outside this range only some integers may be represented exactly
    // this method will use truncation 'rounding mode' during conversion

    // we can safely reinterpret it as 0.0
    if (value == 0) return 0.0;

    if (value == (1U<<31)) // ie -2^31
    
        // -(-2^31) = -2^31 so we'll not be able to handle it below - use const
        // value = 0xCF000000;
        return (float)INT_MIN;  // *((float*)&value); is undefined behaviour
    

    int sign = 0;

    // handle negative values
    if (value < 0)
    
        sign = 1U << 31;
        value = -value;
    

    // although right shift of signed is undefined - all compilers (that I know) do
    // arithmetic shift (copies sign into MSB) is what I prefer here
    // hence using unsigned abs_value_copy for shift
    unsigned int abs_value_copy = value;

    // find leading one
    int bit_num = 31;
    int shift_count = 0;

    for(; bit_num > 0; bit_num--)
    
        if (abs_value_copy & (1U<= 23)
            
                // need to shift right
                shift_count = bit_num - 23;
                abs_value_copy >>= shift_count;
            
            else
            
                // need to shift left
                shift_count = 23 - bit_num;
                abs_value_copy <<= shift_count;
            
            break;
        
    

    // exponent is biased by 127
    int exp = bit_num + 127;

    // clear leading 1 (bit #23) (it will implicitly be there but not stored)
    int coeff = abs_value_copy & ~(1<<23);

    // move exp to the right place
    exp <<= 23;

    union
    
        int rint;
        float rfloat;
    ret =  coeff ;

    return ret.rfloat;

Por supuesto, hay otros medios para encontrar el valor abs de int (sin ramas). De manera similar, el couting de ceros a la izquierda también se puede hacer sin una rama, así que trate este ejemplo como ejemplo ;-).

Te mostramos reseñas y puntuaciones

Tienes la opción de asentar nuestro ensayo dejando un comentario o dejando una valoración te estamos eternamente agradecidos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *