C – Serialización de los números de coma flotante (flotantes, dobles)

5 minutos de lectura

avatar de usuario
psicodelia

¿Cómo convertir un número de punto flotante en una secuencia de bytes para que pueda persistir en un archivo? Tal algoritmo debe ser rápido y altamente portátil. Debe permitir también la operación contraria, la deserialización. Sería bueno si solo se requiere un exceso muy pequeño de bits por valor (espacio persistente).

  • ¿A qué sistemas desea ser portátil?

    – Mark Elliot

    23 de noviembre de 2009 a las 21:55

  • debe ser independiente de la arquitectura subyacente, por ejemplo, puede ser ARM-7, PowerPC, Microblaze, OpenRISC o simplemente x86.

    – psihodelia

    23 de noviembre de 2009 a las 22:05

  • ¿Esto es tarea? De sus comentarios seguro que parece que sí.

    –David Harris

    23 de noviembre de 2009 a las 22:09

  • Algunas preguntas son interesantes incluso en caso de que hayan surgido de la tarea. Simplemente prohibir todos los hechos y temas en este sitio si alguna vez fueron el tema de la tarea de alguien significaría borrar la mitad, supongo…

    – yeoman

    10 de abril de 2017 a las 9:02

Suponiendo que esté utilizando compiladores convencionales, los valores de punto flotante en C y C++ obedecen al estándar IEEE y cuando se escriben en forma binaria en un archivo se pueden recuperar en cualquier otra plataforma, siempre que escriba y lea usando el mismo byte endianess. Así que mi sugerencia es: elija un endianess de elección, y antes de escribir o después de leer, verifique si ese endianess es el mismo que en la plataforma actual; si no, simplemente intercambie los bytes.

  • de acuerdo con la especificación C99, anexo F, las implementaciones conformes deben definir __STDC_IEC_559__que en principio podría usarse como una verificación en tiempo de compilación, pero es inútil en la práctica ya que hay problemas con gcc ( gcc.gnu.org/c99status.html desplácese hacia abajo hasta ‘Otros problemas’)

    – Cristóbal

    23 de noviembre de 2009 a las 22:37

  • Los compiladores no dictan necesariamente el formato de punto flotante IEEE. Todavía hay computadoras que lamentablemente usan otros formatos (VAX/Alpha, IBM). Pero +1 asegurando que tienes el endianness correcto.

    – usuario7116

    26 de noviembre de 2009 a las 2:26

  • Correcto, pero deben conocer el formato que utiliza la plataforma para admitirlo en RTL. Además, muchas plataformas (en la actualidad, especialmente las integradas) no tienen un coprocesador matemático, por lo que dictan el formato en la biblioteca de emulación adjunta. Entonces pensé que sería más fácil referirme al compilador.

    – Fabio Ceconello

    26 de noviembre de 2009 a las 23:24

  • ¿No es el caso tratar esas plataformas que no soportan el estándar IEEE como excepciones, y cuando se necesita la versión (rara) para ellas, simplemente hacer las conversiones necesarias solo allí? Aquí hay un buen artículo sobre las diferencias: codeproject.com/KB/applications/libnumber.aspx

    – Fabio Ceconello

    26 de noviembre de 2009 a las 23:26

Esto podría ser un buen comienzo: empaqueta un valor de punto flotante en un int y long long par, que luego puede serializar de la manera habitual.

#define FRAC_MAX 9223372036854775807LL /* 2**63 - 1 */

struct dbl_packed
{
    int exp;
    long long frac;
};

void pack(double x, struct dbl_packed *r)
{
    double xf = fabs(frexp(x, &r->exp)) - 0.5;

    if (xf < 0.0)
    {
        r->frac = 0;
        return;
    }

    r->frac = 1 + (long long)(xf * 2.0 * (FRAC_MAX - 1));

    if (x < 0.0)
        r->frac = -r->frac;
}

double unpack(const struct dbl_packed *p)
{
    double xf, x;

    if (p->frac == 0)
        return 0.0;

    xf = ((double)(llabs(p->frac) - 1) / (FRAC_MAX - 1)) / 2.0;

    x = ldexp(xf + 0.5, p->exp);

    if (p->frac < 0)
        x = -x;

    return x;
}

avatar de usuario
chris dodd

Siempre puede convertir al formato IEEE-754 en un orden de bytes fijo (ya sea little endian o big endian). Para la mayoría de las máquinas, eso no requeriría nada en absoluto o un simple intercambio de bytes para serializar y deserializar. Una máquina que no admita IEEE-754 de forma nativa necesitará un convertidor escrito, pero hacerlo con ldexp y frexp (funciones estándar de la biblioteca C) y el barajado de bits no es demasiado difícil.

  • El problema viene con los estándares FP que carecen de algunas de las “características” de IEEE. Es decir, los formatos de punto flotante de VAX e IBM… Te encontrarás en un mundo de casos de esquinas dañadas. Afortunadamente, la gente ha escrito excelentes convertidores que manejan estos casos con gracia (¡te estoy mirando USGS! Te debo una cerveza).

    – usuario7116

    26 de noviembre de 2009 a las 2:28

  • Una función frex compatible con ANSI oculta la mayor parte de eso para usted. Por supuesto, puede terminar con casos en los que la serialización y la deserialización le den un valor (cercano pero) diferente.

    – Chris Dodd

    30 de noviembre de 2009 a las 18:35

avatar de usuario
pmg

¿Qué quieres decir con “portátil”?

Para la portabilidad, recuerde mantener los números dentro de los límites definidos en el Estándar: use un solo número fuera de estos límites, y toda la portabilidad se va por el desagüe.

double planck_time = 5.39124E-44; /* second */

5.2.4.2.2 Características de los tipos flotantes

[...]
10   The values given in the following list shall be replaced by constant
     expressions with implementation-defined values [...]
11   The values given in the following list shall be replaced by constant
     expressions with implementation-defined values [...]
12   The values given in the following list shall be replaced by constant
     expressions with implementation-defined (positive) values [...]
[...]

Nota la definido por la implementación en todas estas cláusulas.

Convertir a una representación ascii sería lo más simple, pero si necesita lidiar con una cantidad colosal de flotantes, entonces, por supuesto, debe usar el sistema binario. Pero esto puede ser un problema complicado si te preocupa la portabilidad. Los números de punto flotante se representan de manera diferente en diferentes máquinas.

Si no desea utilizar una biblioteca enlatada, entonces su serializador/deserializador binario flotante simplemente tendrá que tener “un contrato” sobre dónde aterriza cada bit y qué representa.

Aquí hay un sitio web divertido para ayudar con eso: Enlace.

sprintf, fprintf ? no hay nada más portátil que eso.

avatar de usuario
david harris

¿Qué nivel de portabilidad necesita? Si el archivo se va a leer en una computadora con el mismo sistema operativo en el que se generó, debe usar un archivo binario y simplemente guardar y restaurar el patrón de bits. De lo contrario, como dijo boytheo, ASCII es tu amigo.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad