Codificación del valor int como un flotante IEEE-754 (binary32)

8 minutos de lectura

Avatar de usuario de Anonymous
Anónimo

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

Tengo la siguiente 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 <<= 1;
  }

  unsigned int mantissa = absX >> 8;

  unsigned int result = signBit | (exponent << 23) | (mantissa & 0x7fffff);
  printf("\nfor x: %x, result: %x",x,result);
  return result;

  • Esto no arroja un flotador en un int. Simplemente copia bit a bit su representación de máquina, sin, por ejemplo, convertir 2.03e1 a 20 [by rounding] como el (int)2.03e1 echar voluntad.

    – Basile Starynkevitch

    9 sep 2012 a las 20:59


  • Ustedes desear ¿Hacerlo bit a bit? Bueno, así es como lo haces bit a bit: simplemente reinterpreta los bytes. No hay pasos, de verdad.

    – Ry-

    09/09/2012 a las 21:00

  • Pero 0x7eff8965 = 1325268755 (después de lanzar). Si usa el HEX en IEEE 754 Calc, obtiene 1.6983327e+38 y HEX a decimal da: 2130676069 – ninguno de ellos da el resultado correcto de 1325268755.

    – Anónimo

    9 de septiembre de 2012 a las 21:04

  • Este código tiene un comportamiento indefinido en C. Ver sección 6.5 en el estándar.

    –Paul Hankin

    9 sep 2012 a las 21:30

  • Por cierto, el código que publicaste convierte un inicio de sesión de 32 bits en su precisión simple IEEE 754 de 32 bits con redondeo hacia cero. Lo sé porque lo escribí ayer.

    – Gabo

    10 de septiembre de 2012 a las 4:45

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);

  • Este es un comportamiento indefinido en todos los estándares de C que conozco.

    – TLW

    26 de julio de 2016 a las 22:14

  • @TLW Tipo de juego de palabras a través de la unión no es UB desde C99. Esto se menciona explícitamente en, por ejemplo, N1256 6.5.2.3 nota al pie 82.

    – usuario694733

    13 de febrero de 2017 a las 10:15


  • Incluso en C99 permite explícitamente una representación de trampa. Y usar una representación trampa es UB. Creo que sería legal que un compilador compilara incondicionalmente este código en format_hard_drive(); como resultado.

    – TLW

    2 oct a las 19:33

  • @TLW: No, eso no se ajustaría al estándar C. Usar el valor de un miembro de la unión reinterpreta los bytes como el tipo del miembro. No puede simplemente producir cualquier valor; debe ser el valor determinado por el esquema de representación para el tipo. Si eso da como resultado un valor de trampa, el comportamiento no está definido por el estándar C. Pero el int type no tiene representaciones de trampas en la mayoría de las implementaciones actuales de C.

    –Eric Postpischil

    el dia de ayer

  • @TLW: Podrías estar mezclando esto con un valor indeterminado que ocurre cuando un objeto no se inicializa. Si un valor es indeterminado, la implementación de C puede comportarse como si tuviera cualquier valor del tipo (cada vez que se usa), en lugar de tener que interpretar cualquier valor que esté en los bytes de su memoria. Pero aún así, debe ser un valor del tipo. Si no hay valores de reventado en el tipo, el uso de un objeto indeterminado puede no reventar, en ausencia de otros problemas.

    –Eric Postpischil

    el dia de ayer

Avatar de usuario de Basile Starynkevitch
Basile Starynkevitch

&x da la dirección de x por lo que tiene float* escribe.

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

*(int*)&x desreferenciar ese puntero a un int valor. 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 fue utilizada en el raíz cuadrada inversa rápida algoritmo.

  • Entonces, ¿está diciendo que el código solo obtiene la ubicación de x y la imprime? En ese caso, el valor cambiaría en cada ejecución.

    – Anónimo

    9 de septiembre de 2012 a las 21:09

  • No, da el número entero contenido en la ubicación del flotador, entonces, cuando sizeof(int) == sizeof[float] da el int de la misma representación de bits de máquina que su x ; no se imprime nada a menos que llame a una rutina de impresión como printf (que no está en tu pregunta)

    – Basile Starynkevitch

    9 de septiembre de 2012 a las 21:18


  • Ok, entonces da el valor almacenado en la ubicación en la memoria y lo convierte en un tipo int. ¿Cómo puedo hacer esto sin hacer casting?

    – Anónimo

    9 sep 2012 a las 21:27

  • @BasileStarynkevitch: ¿cuál sería el problema con endianness? Si solo está buscando seleccionar los bits de un flotador, no creo que importe si los ints se almacenan en big-endian o little-endian.

    –Björn Lindqvist

    21/01/2018 a las 16:00

  • Endianness sería un problema si estuviera convirtiendo un flotante en un int sin firmar, donde está usando los bits como indicadores y la función/programa/dispositivo de envío solo puede enviar flotantes.

    –Mark Walsh

    15 de enero de 2019 a las 20:37

// With the proviso that your compiler implementation uses
// the same number of bytes for an int as for a float:
// example float
float f = 1.234f;
// get address of float, cast as pointer to int, reference
int i = *((int *)&f);
// get address of int, cast as pointer to float, reference
float g = *((float *)&i);
printf("%f %f %08x\n",f,g,i);

  • Esto no agrega nada a las respuestas anteriores.

    – Matthieu Brucher

    23 de noviembre de 2018 a las 14:36

  • No estoy de acuerdo. Es un buen ejemplo independiente, el Sr. Brucher con una reputación de 6923

    – Dino Dini

    23 de noviembre de 2018 a las 15:40

  • ¿Esto no viola la estricta regla de aliasing?

    – palapapa

    19 de noviembre a las 9:25

avatar de usuario de wildplaser
salvaje

float x = 43.133;
int y;

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

Avatar de usuario de Johan Köhler
johan köhler

Puedes lanzar el flotador usando una referencia. Un lanzamiento como este nunca debería generar ningún código.

C++

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

Producción:

Float 1.000000 is 0x3f800000

Si desea una conversión de estilo c ++, use un reinterpret_cast, como este.

int i = reinterpret_cast<int &>(f);

No funciona con expresiones, hay 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 &'

  • Agregue información sobre cómo funciona su código

    – Asesino de koopas

    11/08/2015 a las 21:01

  • Su segundo ejemplo funcionará si usa una referencia rvalue, reemplace (int&) con (int&&). Esto es necesario ya que la expresión devuelve una referencia de valor r a la que no se pueden vincular las referencias de valor l. Supongo que también podrías usar (const int &) para unir a ambos.

    – nitronoide

    19 de agosto de 2017 a las 10:34

Avatar de usuario de Alex Brown
Alex Brown

No puede (significativamente) convertir un número de coma flotante en un ‘entero’ (signed int o int) De este modo.

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

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


También hay problemas de la plataforma con manipulación de bits de entradas firmadas.

  • Agregue información sobre cómo funciona su código

    – Asesino de koopas

    11/08/2015 a las 21:01

  • Su segundo ejemplo funcionará si usa una referencia rvalue, reemplace (int&) con (int&&). Esto es necesario ya que la expresión devuelve una referencia de valor r a la que no se pueden vincular las referencias de valor l. Supongo que también podrías usar (const int &) para unir a ambos.

    – nitronoide

    19 de agosto de 2017 a las 10:34

Multiplique el número flotante por el factor que desee. En este caso multipliqué por 100,000, porque 5 decimales después de fracción tienen significado en mi operación.

Conviértalo a bytes y luego únalos y divida por 100,000 nuevamente.

double angleX, angleY;
angleX = 3.2342;
angleY = 1.34256;
printf("%f, %f", (double)angleX, (double)angleY);

int remain, j;
int TxData[8];
j=0;
remain=0;
unsigned long data = angleX*100000;
printf("\ndata : %d\n", data);

while(data>=256)
{
    remain= data%256;
    data = data/256;
    TxData[j]= remain;
    printf("\ntxData %d : %d", j, TxData[j]);
    j++;
}
TxData[j] = data;
printf("\ntxData %d : %d", j, TxData[j]);

int i=0;
long int angleSon=0;
for(i=0;i<=j;i++)
{

    angleSon += pow(256,i)*TxData[i]; 
    printf("\nangleSon : %li", angleSon);
}   

¿Ha sido útil esta solución?