Intereting Posts
Cuda C – Ошибка компоновщика – неопределенная ссылка Понимание оптимизации компилятора MSVS C ++ C символьные значения арифметики Выполнение арифметических операций в двоичном режиме с использованием только побитовых операторов Расположение указателей и глобальных переменных в C Как переменные связаны с их значениями в C? Что создает posix_fadvise () для записи последовательного файла? Ошибка сегментации при изменении строки Могу ли я искать каталог и подкаталоги для файлов заголовков? Возможно ли иметь связанный список разных типов данных? В многопоточном приложении, как я могу перенаправить stderr & stdout в отдельный файл в соответствии с streamом? Спецификаторы формата для типов, определенных для реализации, таких как time_t Типовые универсальные контейнеры с макросами Передача указателя на локальную переменную: она безопасна? Возможно ли, чтобы fscanf возвращал ноль и потреблял вход одновременно?

Переносная сериализация значений с плавающей запятой IEEE754

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

|-- 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 , сделать это?

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

Гораздо проще, и в зависимости от того же предположения, что и у вас (это то, что типы float и integer имеют один и тот же порядок байтов и почти универсальны – реально вы никогда не столкнетесь с системой, где это неверно):

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

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

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

 #include  float htonf (float val) { union ieee754_float u; float v; uint8_t *un = (uint8_t *) &v; uf = 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 uf; } 

Ниже приведена переносимая процедура записи 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); }