我最近一直致力于一个需要存储和加载大量数据的系统,包括单精度浮点值。我决定对整数的网络字节顺序进行标准化,并决定以big-endian格式存储浮点值,即:
|-- Byte 0 --| |-- Byte 1 -| Byte 2 Byte 3
# ####### # ####### ######## ########
Sign Exponent Mantissa
1b 8b, MSB first 23b, MSB first
理想情况下,我想提供像htonl()
和ntohl()
这样的函数,因为我已经使用这些函数来擦除整数,而且我也希望以一种具有尽可能多的平台的方式实现它 - 尽可能独立(假设float
类型对应于IEEE754 32位浮点值)。有没有办法,可能使用ieee754.h
来做到这一点?
我有一个似乎工作的答案,我将在下面发布,但它似乎相当缓慢且效率低下,我将不胜感激任何有关如何使其更快和/或更可靠的建议
答案 0 :(得分: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
一样高,加上单个函数调用的开销。
答案 1 :(得分:1)
正如上面提到的问题所述,我对我的问题有一个解决方案,但我并不特别依赖它,我欢迎其他答案,所以我在这里发布而不是在问题中。特别是,它似乎很慢,我不确定它是否会打破严格的混淆,以及其他潜在的问题。
#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;
}
答案 2 :(得分: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);
}