Casting float to int (bitwise) en C

Dados los 32 bits que representan un número de coma flotante IEEE 754, ¿cómo se puede convertir el número a un número entero, usando operaciones de enteros o bits en la representación (en lugar de usar una instrucción de máquina o una operación de comstackción para convertir)?

EDIT # 1:

Tengo que seguir la función pero falla en algunos casos:

Entrada: int x (contiene un número de precisión simple de 32 bits en formato IEEE 754)

if(x == 0) return x; unsigned int signBit = 0; unsigned int absX = (unsigned int)x; if (x < 0) { signBit = 0x80000000u; absX = (unsigned int)-x; } unsigned int exponent = 158; while ((absX & 0x80000000) == 0) { exponent--; absX <> 8; unsigned int result = signBit | (exponent << 23) | (mantissa & 0x7fffff); printf("\nfor x: %x, result: %x",x,result); return result; 

EDIT # 2:

También necesita ayuda con: https://cs.stackexchange.com/questions/3484/converting-function-to-bitwise-only

C tiene la “unión” para manejar este tipo de vista de datos:

 typedef union { int i; float f; } u; u u1; u1.f = 45.6789; /* now u1.i refers to the int version of the float */ printf("%d",u1.i); 

(Alguien debería verificar dos veces esta respuesta, especialmente los casos de borde y el redondeo de valores negativos. Además, lo escribí para redondear a más cercano. Para reproducir la conversión de C, esto debería cambiarse a redondear-hacia-cero).

Esencialmente, el proceso es:

Separe los 32 bits en un bit de signo ( s ), ocho bits de exponente ( e ) y 23 bits de significado ( f ). Trataremos estos como enteros de complemento doble.

Si e es 255, el objeto de coma flotante es infinito (si f es cero) o NaN (de lo contrario). En este caso, la conversión no se puede realizar y se debe informar un error.

De lo contrario, si e no es cero, agregue 2 24 a f . (Si e no es cero, el significado tiene implícitamente 1 bit en su parte frontal. Agregar 2 24 hace que ese bit sea explícito en f .)

Reste 127 de e . (Esto convierte el exponente de su forma sesgada / codificada al exponente real. Si estuviéramos haciendo una conversión general a cualquier valor, tendríamos que manejar el caso especial cuando e es cero: resta 126 en lugar de 127. Pero, dado que solo estamos convirtiendo a un resultado entero, podemos descuidar este caso, siempre y cuando el resultado entero sea cero para estos pequeños números de entrada).

Si s es 0 (el signo es positivo) y e es 31 o más, entonces el valor desborda un entero de 32 bits con signo (es 2 31 o más). La conversión no se puede realizar y se debe informar un error.

Si s es 1 (el signo es negativo) y e es más de 31, entonces el valor rebasa un entero de 32 bits con signo (es menor o igual a -2 32 ). Si s es uno, e es 32 yf es mayor que 2 24 (cualquiera de los bits significativos originales se establecieron), entonces el valor rebasa un entero de 32 bits con signo (es menor que -2 31 ; si el f original eran cero, sería exactamente -2 31 , que no se desborda). En cualquiera de estos casos, no se puede realizar la conversión y se debe informar un error.

Ahora tenemos una s , una ey una f para un valor que no se desborda, por lo que podemos preparar el valor final.

Si s es 1, configure f a – f .

El valor del exponente es para un significado entre 1 (inclusive) y 2 (exclusivo), pero nuestro significado comienza con un bit de 2 24 . Entonces tenemos que ajustarnos para eso. Si e es 24, nuestro significado es correcto, y hemos terminado, entonces devuelve f como resultado. Si e es mayor que 24 o menor que 24, tenemos que cambiar el significado apropiadamente. Además, si vamos a desplazar f a la derecha, tendremos que redondearlo, para obtener un resultado redondeado al número entero más cercano.

Si e es mayor que 24, cambie f left e -24 bits. Devuelve f como resultado.

Si e es menor que -1, el número de coma flotante está entre -½ y ½, exclusivo. Devuelve 0 como resultado.

De lo contrario, cambiaremos f derecha 24 e bits. Sin embargo, primero guardaremos los bits que necesitamos para redondear. Establezca r en el resultado de convertir f en un entero de 32 bits sin signo y desplazándolo a la izquierda en 32- (24 e ) bits (equivalentemente, a la izquierda en 8 o más bits). Esto toma los bits que se desplazarán fuera de f (abajo) y “los ajusta a la izquierda” en los 32 bits, por lo que tenemos una posición fija donde comienzan.

Shift f right 24 e bits.

Si r es menor que 2 31 , no haga nada (esto se redondea hacia abajo, el cambio trunca los bits). Si r es mayor que 2 31 , agregue uno a f (esto se redondea). Si r es igual a 2 31 , agregue el bit bajo de f a f . (Si f es impar, agregue uno a f . De los dos valores igualmente cercanos, este se redondea al valor par.) Devuelve f .

&x da la dirección de x, por lo que tiene el tipo float* .

(int*)&x arrojar ese puntero a un puntero a int es decir, a una cosa int* .

*(int*)&x desreferencia ese puntero en un valor int . No hará lo que usted cree en máquinas donde int y float tienen diferentes tamaños.

Y podría haber problemas de endianness.

Esta solución se usó en el algoritmo de raíz cuadrada inversa rápida .

No puede (de manera significativa) convertir un número de punto flotante en un ‘entero’ ( signed int o int ) de esta manera.

Puede terminar teniendo el tipo de entero, pero en realidad es solo un índice en el espacio de encoding de IEEE754, no es un valor significativo en sí mismo.

Se podría argumentar que una int unsigned tiene doble propósito como un patrón de bits y un valor entero, pero int no lo tiene.


También hay problemas de plataforma con la manipulación de bit de ints firmados.

 float x = 43.133; int y; assert (sizeof x == sizeof y); memcpy (&y, &x, sizeof x); ... 

Puede lanzar el flotador usando una referencia. Un elenco como este nunca debe generar ningún código.

C ++

 float f = 1.0f; int i = (int &)f; printf("Float %f is 0x%08x\n", f, i); 

Salida:

 Float 1.000000 is 0x3f800000 

Si quieres un elenco de estilo c ++ utiliza reinterpret_cast, como este.

 int i = reinterpret_cast(f); 

No funciona con expresiones, tienes que almacenarlo en una variable.

  int i_times_two; float f_times_two = f * 2.0f; i_times_two = (int &)f_times_two; i_times_two = (int &)(f * 2.0f); main.cpp:25:13: error: C-style cast from rvalue to reference type 'int &'