2012-05-16 5 views
10

Я недавно работал над системой, которая должна хранить и загружать большое количество данных, включая значения с плавающей запятой с одинарной точностью. Я решил стандартизировать сетевой порядок байт для целых чисел, а также решил хранить значения с плавающей запятой в тупоконечника формата, то есть:Переносная сериализация значений с плавающей запятой IEEE754

|-- Byte 0 --| |-- Byte 1 -| Byte 2 Byte 3 
    #  ####### #  ####### ######## ######## 
Sign  Exponent   Mantissa 
1b 8b, MSB first 23b, MSB first 

В идеале, я хочу, чтобы обеспечить такие функции, как htonl() и ntohl(), так как у меня уже использовали их для целых чисел swabing, и я также хочу реализовать их таким образом, чтобы максимально возможная независимость от платформы (при условии, что тип float соответствует 32-разрядным значениям с плавающей запятой IEEE754). Есть ли способ, возможно, используя ieee754.h?

У меня есть один ответ, который кажется, и я отправлю его ниже, но он выглядит довольно медленным и неэффективным, и я был бы признателен за любые предложения о том, как сделать его быстрее и/или более надежным.

+0

Об этом: http://stackoverflow.com/a/2782742/1327576? – smocking

+0

Я посмотрел на этот ответ, и ясно, что это зависит от предположения, что представление хоста малопригодно. Я ищу что-то, что связано с хостом-байтом-агностиком. –

+0

Возможно, 'snprintf (b, sizeof (b),"% .9001f ", yourvalue)' (текстовое представление) является наиболее переносимым. –

ответ

6

Намного проще, и в зависимости от того же предположения, как ваш (который является то, что с плавающей точкой и целые типы имеют одинаковый порядок байт, и является почти общеобязательной - реально вы никогда не будете сталкиваться системе, где это не так):

#include <string.h> 

float htonf(float val) { 
    uint32_t rep; 
    memcpy(&rep, &val, sizeof rep); 
    rep = htonl(rep); 
    memcpy(&val, &rep, sizeof rep); 
    return val; 
} 

Любой достаточно хороший компилятор оптимизировать на два memcpy вызовов; они присутствуют, чтобы победить чрезмерную строгую оптимизацию псевдонимов, поэтому это заканчивается так же эффективно, как и htonl, плюс накладные расходы на один вызов функции.

+0

Спасибо за ваш ответ. Просто чтобы убедиться, что я понимаю - это предположение о байтовом заказе в моем ответе из-за использования 'ieee754.h'? –

+1

Да, 'ieee754.h' предполагает, что - или, по крайней мере, все реализации, которые я видел, (и, как я уже сказал, это может быть общепринятым предположением в нашу современную эпоху). –

+1

Не какие-то архитектуры ARM имеют странное смехотворное сумасшествие? Во всяком случае, еще раз спасибо за ваш ответ - это намного легче понять, чем мое, по крайней мере! –

0

Как уже упоминалось в вышеприведенном вопросе, у меня есть решение проблемы, но я не очень привязан к ней, и я приветствую другие ответы, поэтому я размещаю ее здесь, а не в вопросе. В частности, это, вероятно, будет медленным, и я не уверен, что он нарушает строгий псевдоним, среди других потенциальных проблем.

#include <ieee754.h> 

float 
htonf (float val) 
{ 
    union ieee754_float u; 
    float v; 
    uint8_t *un = (uint8_t *) &v; 

    u.f = val; 
    un[0] = (u.ieee.negative << 7) + ((u.ieee.exponent & 0xfe) >> 1); 
    un[1] = ((u.ieee.exponent & 0x01) << 7) + ((u.ieee.mantissa & 0x7f0000) >> 16); 
    un[2] = (u.ieee.mantissa & 0xff00) >> 8; 
    un[3] = (u.ieee.mantissa & 0xff); 
    return v; 
} 

float 
ntohf (float val) 
{ 
    union ieee754_float u; 
    uint8_t *un = (uint8_t *) &val; 

    u.ieee.negative = (un[0] & 0x80) >> 7; 
    u.ieee.exponent = (un[0] & 0x7f) << 1; 
    u.ieee.exponent += (un[1] & 0x80) >> 7; 
    u.ieee.mantissa = (un[1] & 0x7f) << 16; 
    u.ieee.mantissa += un[2] << 8; 
    u.ieee.mantissa += un[3]; 

    return u.f; 
} 
+0

Я не думаю, что это медленно. –

+1

Спасибо за доверие. Я упоминал, что имею дело с * лотом * данных? ;-) Что заставляет вас смотреть быстро? –

+0

Он не использует дорогие операции, никаких циклов, никаких прыжков. И я не могу представить, как вы могли бы уйти со значительно меньшими операциями. Но мне по крайней мере так же любопытно, как вы, чтобы увидеть лучшие предложения. –

1

Вот ручная рутинная программа записи IEEE 754. Он будет писать двойной в формате IEEE 754, независимо от представления с плавающей запятой на главной машине.

/* 
* write a double to a stream in ieee754 format regardless of host 
* encoding. 
* x - number to write 
* fp - the stream 
* bigendian - set to write big bytes first, elee write litle bytes 
*    first 
* Returns: 0 or EOF on error 
* Notes: different NaN types and negative zero not preserved. 
*   if the number is too big to represent it will become infinity 
*   if it is too small to represent it will become zero. 
*/ 
static int fwriteieee754(double x, FILE *fp, int bigendian) 
{ 
    int shift; 
    unsigned long sign, exp, hibits, hilong, lowlong; 
    double fnorm, significand; 
    int expbits = 11; 
    int significandbits = 52; 

    /* zero (can't handle signed zero) */ 
    if (x == 0) 
    { 
     hilong = 0; 
     lowlong = 0; 
     goto writedata; 
    } 
    /* infinity */ 
    if (x > DBL_MAX) 
    { 
     hilong = 1024 + ((1 << (expbits - 1)) - 1); 
     hilong <<= (31 - expbits); 
     lowlong = 0; 
     goto writedata; 
    } 
    /* -infinity */ 
    if (x < -DBL_MAX) 
    { 
     hilong = 1024 + ((1 << (expbits - 1)) - 1); 
     hilong <<= (31 - expbits); 
     hilong |= (1 << 31); 
     lowlong = 0; 
     goto writedata; 
    } 
    /* NaN - dodgy because many compilers optimise out this test, but 
    *there is no portable isnan() */ 
    if (x != x) 
    { 
     hilong = 1024 + ((1 << (expbits - 1)) - 1); 
     hilong <<= (31 - expbits); 
     lowlong = 1234; 
     goto writedata; 
    } 

    /* get the sign */ 
    if (x < 0) { sign = 1; fnorm = -x; } 
    else { sign = 0; fnorm = x; } 

    /* get the normalized form of f and track the exponent */ 
    shift = 0; 
    while (fnorm >= 2.0) { fnorm /= 2.0; shift++; } 
    while (fnorm < 1.0) { fnorm *= 2.0; shift--; } 

    /* check for denormalized numbers */ 
    if (shift < -1022) 
    { 
     while (shift < -1022) { fnorm /= 2.0; shift++; } 
     shift = -1023; 
    } 
    /* out of range. Set to infinity */ 
    else if (shift > 1023) 
    { 
     hilong = 1024 + ((1 << (expbits - 1)) - 1); 
     hilong <<= (31 - expbits); 
     hilong |= (sign << 31); 
     lowlong = 0; 
     goto writedata; 
    } 
    else 
     fnorm = fnorm - 1.0; /* take the significant bit off mantissa */ 

    /* calculate the integer form of the significand */ 
    /* hold it in a double for now */ 

    significand = fnorm * ((1LL << significandbits) + 0.5f); 


    /* get the biased exponent */ 
    exp = shift + ((1 << (expbits - 1)) - 1); /* shift + bias */ 

    /* put the data into two longs (for convenience) */ 
    hibits = (long)(significand/4294967296); 
    hilong = (sign << 31) | (exp << (31 - expbits)) | hibits; 
    x = significand - hibits * 4294967296; 
    lowlong = (unsigned long)(significand - hibits * 4294967296); 

writedata: 
    /* write the bytes out to the stream */ 
    if (bigendian) 
    { 
     fputc((hilong >> 24) & 0xFF, fp); 
     fputc((hilong >> 16) & 0xFF, fp); 
     fputc((hilong >> 8) & 0xFF, fp); 
     fputc(hilong & 0xFF, fp); 

     fputc((lowlong >> 24) & 0xFF, fp); 
     fputc((lowlong >> 16) & 0xFF, fp); 
     fputc((lowlong >> 8) & 0xFF, fp); 
     fputc(lowlong & 0xFF, fp); 
    } 
    else 
    { 
     fputc(lowlong & 0xFF, fp); 
     fputc((lowlong >> 8) & 0xFF, fp); 
     fputc((lowlong >> 16) & 0xFF, fp); 
     fputc((lowlong >> 24) & 0xFF, fp); 

     fputc(hilong & 0xFF, fp); 
     fputc((hilong >> 8) & 0xFF, fp); 
     fputc((hilong >> 16) & 0xFF, fp); 
     fputc((hilong >> 24) & 0xFF, fp); 
    } 
    return ferror(fp); 
} 
Смежные вопросы