我需要使用套接字传输双精度值(例如-47.1235648)。由于我将有很多平台,因此我必须转换为网络字节顺序以确保所有端的正确字节序。但是这个转换不接受双精度,只接受整数和短整型,所以我将我的双精度"切割"成两个整数进行传输,如下所示:
double lat = -47.848945;
int a;
int b;
a = (int)lat;
b = (int)(lat+1);
现在,我需要在另一端恢复它,但尽可能使用最少的计算(我看到了一些使用 POW 的例子,但看起来 pow 为此使用了大量资源,我不确定)。有没有办法尽可能简单地加入它,比如位操作?
你的代码没有意义。
典型的方法是使用memcpy()
:
const double lat = -47.848945;
uint32_t ints[sizeof lat / sizeof (uint32_t)];
memcpy(ints, &lat, sizeof lat);
现在发送ints
的元素,它们只是 32 位无符号整数。
这当然假设:
- 您知道如何以安全的方式发送
uint32_t
,即每字节字节或使用字节序转换函数。 - 所有主机共享相同的二进制
double
格式(通常为 IEEE-754)。 - 当从一对整数移动到/移动到单个
double
值时,您可以以某种方式管理字节顺序要求(请参阅@JohnBollinger的答案)。
我把你的问题解释为所有这些假设都是安全的,这可能有点过头了。只要接受这个答案,我就不能删除。
您正在考虑数字表示的差异是件好事,但是您关于如何处理此问题的想法并不可靠。
假设所涉及的每台机器都使用 64 位 IEEE-754 格式作为其double
表示形式。 (这已经是一个潜在的故障点,但实际上你可能不必担心那里的故障。 您似乎假设机器double
的字节顺序将以一致的方式映射到其整数的字节顺序,但这不是一个安全的假设。 此外,即使这个假设成立,你也需要完全正确的映射才能让你的方案工作,这不仅不安全,而且很可能不是你实际看到的。
为了便于论证,假设具有大端整数的机器 A 想要将double
值传输到具有小端整数的机器 B。 进一步假设在 B 上,其double
表示的字节顺序与 A 上的顺序完全相反(同样,假设这是不安全的)。 因此,如果在 A 上,则该双精度的字节按顺序排列
S T U V W X Y Z
然后我们希望它们井井有条
Z Y X W V U T S
在 B 上。 您的方法是将原始文件拆分为一对(STUV,WXYZ),以保值方式将货币对转移以获得(VUTS,ZYXW),然后将该对重新组合在一起以获得...呃哦...
V U T S Z Y X W
. 不要想象通过首先交换货币对来解决这个问题。 这不符合您的目的,因为您必须避免在两台通信计算机具有相同字节顺序的情况下进行此类交换,并且您无法仅从 8 个字节中知道是否需要此类交换。 因此,即使我们做出简单的假设,我们知道是不安全的,你的策略也不足以完成这项任务。
替代方案包括:
- 将你的双打作为字符串转移。
- 将双精度值传输为整数(有效数、小数)对。
frexp()
和ldexp()
函数可以帮助编码和解码此类表示。 - 传输双精度的基于整数的定点表示(与上一个选项相同,但具有未传输的预定比例)
我需要使用套接字传输双精度值(例如 -47.1235648)。
如果平台对double
有潜在的不同编码,那么发送double
的位模式是一个问题。 如果代码需要可移植性,则需要一种"仅复制位"的方法。 下面是另一种选择。
如果平台始终具有相同的double
格式,只需复制n
位即可。 示例:@Rishikesh拉杰
详细地说,OP的问题只是松散的定义。 在许多平台上,double
是二进制64,但C不需要这样做。该double
可以精确地表示大约 264个不同的值。 -47.1235648 和 -47.848945 都不是其中之一。 因此,OP可能没有很强的精度问题。
"尽可能使用最少的计算"意味着最少的代码,通常要有最少的时间。 为了提高速度,任何解决方案都应按复杂程度和代码分析进行评级。
一种可移植的方法是通过字符串发送。 这种方法首先解决正确性和最佳精度,其次才是性能问题。 它消除了字节序问题,因为数据是通过字符串发送的,并且在发送数据时没有精度/范围损失。 接收方,如果使用相同的double
格式将重新形成double
。 对于不同的double
机器,它具有良好的字符串表示形式,可以尽其所能。
// some ample sized buffer
#define N (sizeof(double)*CHAR_BIT)
double x = foo();
char buf[N];
#if FLT_RADIX == 10
// Rare based 10 platforms
// If macro DBL_DECIMAL_DIG not available, use (DBL_DIG+3)
sprintf(buf, "%.*e", DBL_DECIMAL_DIG-1, x);
#else
// print mantissa in hexadecimal notation with power-of-2 exponent
sprintf(buf, "%a", x);
#endif
bar_send_string(buf);
重组double
char *s = foo_get_string();
double y;
// %f decode strings in decimal(f), exponential(e) or hexadecimal/exponential notation(a)
if (sscanf(s, "%f", &y) != 1) Handle_Error(s);
else use(y);
更好的主意是直接将双精度作为网络字节顺序中的 8 个字节发送。
您可以使用联合
typedef union
{
double a;
uint8_t bytes[8];
} DoubleUnionType;
DoubleUnionType DoubleUnion;
//Assign the double by
DoubleUnion.a = -47.848945;
然后就可以做一个网络字节顺序转换函数了
void htonfl(uint8_t *out, uint8_t *in)
{
#if LITTLE_ENDIAN // Use macro name as per architecture
out[0] = in[7];
out[1] = in[6];
out[2] = in[5];
out[3] = in[4];
out[4] = in[3];
out[5] = in[2];
out[6] = in[1];
out[7] = in[0];
#else
memcpy (out, in, 8);
#endif
}
并在传输前和接收后调用此功能。