从 tcp 读取浮点数
Read floats from tcp
我正在尝试读取从 Python tcp 流服务器中的打包结构传递的 big-endian 8 32 位浮点数。它似乎接近工作,但前几个值有奇数的偏差,后面的值似乎有少量偏差,或者只是不精确。
例如,这些是客户端解释的值:
Val[0] -1926.34
Val[1] -1936.86
Val[2] -1901.15
Val[3] -1935.93
Val[4] -148932
Val[5] -145905
Val[6] -41580.8
Val[7] -134330
这里是(接近但现在相当)它们应该是的值。这不是真实的,因为很难在服务器和客户端上捕获完全相同的数据包。
Val[0] -7737.77159902711
Val[1] -7746.444075875769
Val[2] -7638.46279841218
Val[3] -7776.037785534595
Val[4] -148935.79768369172
Val[5] -145903.3365134402
Val[6] -41594.9200504923
Val[7] -134328.9103304041
这是我的代码:
int size = 32;
char buffer[size];
float vals[8];
int count = 0;
int t;
// Receive a reply from the server
if (recv(sock, buffer, size, 0) < 0) {std::cout << "Receive failed..." << std::endl;}
for (int i = 0; count < 8; i += 4, count++) {
t =
(buffer[i+3]) +
(buffer[i+2] << 8) +
(buffer[i+1] << 16) +
(buffer[i] << 24);
vals[count] = *reinterpret_cast<float*>(&t);
}
Python 服务器发送数据包:
packer = struct.Struct('>%sf' % 8)
packed_data = packer.pack(*values)
sock.send(packed_data)
我感觉这可能是关于 int 和 float 之间转换的问题,但我似乎无法弄清楚。非常感谢任何帮助。
buffer
正在使用 char
,它可能已在您的系统上签名。
如果 buffer[0]
是 -1
,那么它会转换为 -1
作为 int
(不再是 char
)。这在将所有这些字节加在一起时会导致问题(因为 -1
作为 char
可能是 0xff
,而作为 int
可能是 0xffffffff
)。换句话说,如果缓冲区包含具有负值的字节,由于整数提升,这将搞砸 "merging" 字节。
你在那里的重新解释打破了严格的别名规则。这很可能适用于您可能使用的任何系统,但严格来说是未定义的行为。
您可以将其替换为将字节直接分配给 float
的代码,如下所示:
int size = 32;
char buffer[size];
float vals[8];
int count = 0;
int t;
// Receive a reply from the server
if (recv(sock, buffer, size, 0) < 0) {std::cout << "Receive failed..." << std::endl;}
for (int i = 0; count < 8; i += 4, count++) {
char* ptr = (char*)(vals + count);
// switch endianness as needed (though unlikely)
// (this is effectively equivalent to std::memcpy(vals, buffer, size))
ptr[0] = buffer[0];
ptr[1] = buffer[1];
ptr[2] = buffer[2];
ptr[3] = buffer[3];
}
更好的是(为了避免不必要的内存复制),如果你知道两台机器使用相同的浮点格式和大小,你可以用 recv
:
// read the bytes directly into the floats
recv(sock, vals, size, 0);
您的代码正在使用 char
s 进行位操作,这在默认情况下对 char
进行签名的平台(大多数平台)上可能会出现问题。使用 unsigned char 更适合这种用途。
此外,您没有正确实现从套接字读取。 recv
调用不能保证获得您请求的数据量...换句话说,传递的大小只是可以读取的 maximum 数量并且有效接收的数据可能会更少。
从套接字读取时需要进行循环,在收到所有数据或结果为负数或零时退出(负数表示错误,零表示另一端已关闭端点,不再数据来了)。
char buffer[size];
...
t =
(buffer[i+3]) +
(buffer[i+2] << 8) +
(buffer[i+1] << 16) +
(buffer[i] << 24);
这不是字节交换 32 位整数的正确方法。如果 char
被签名了怎么办? (如果您使用 Windows、linux 或 OSX,就是这种情况。)
正确的方法是使用function/macrontohl
。虽然这不是 C 或 C++ 标准的一部分,但您会在任何有望在 Internet 上运行的小端系统上找到它。在 Unix 机器上,您需要 #include <arpa/inet.h>
。在 Windows,您需要 #include <winsock2.h>
.
另一种选择是不要给自己造成这种痛苦。从某种意义上说,这完全是您自己通过在 python 脚本中使用 packer = struct.Struct('>%sf' % 8)
来完成的。您显然知道目标将 运行 在小端机器上,因此将 Struct
构造函数中的 >
更改为“<”。如果您知道 python 脚本将 运行 与您的 C++ 应用程序在同一台机器上,请将 >
更改为 @
或 =
。
我正在尝试读取从 Python tcp 流服务器中的打包结构传递的 big-endian 8 32 位浮点数。它似乎接近工作,但前几个值有奇数的偏差,后面的值似乎有少量偏差,或者只是不精确。
例如,这些是客户端解释的值:
Val[0] -1926.34
Val[1] -1936.86
Val[2] -1901.15
Val[3] -1935.93
Val[4] -148932
Val[5] -145905
Val[6] -41580.8
Val[7] -134330
这里是(接近但现在相当)它们应该是的值。这不是真实的,因为很难在服务器和客户端上捕获完全相同的数据包。
Val[0] -7737.77159902711
Val[1] -7746.444075875769
Val[2] -7638.46279841218
Val[3] -7776.037785534595
Val[4] -148935.79768369172
Val[5] -145903.3365134402
Val[6] -41594.9200504923
Val[7] -134328.9103304041
这是我的代码:
int size = 32;
char buffer[size];
float vals[8];
int count = 0;
int t;
// Receive a reply from the server
if (recv(sock, buffer, size, 0) < 0) {std::cout << "Receive failed..." << std::endl;}
for (int i = 0; count < 8; i += 4, count++) {
t =
(buffer[i+3]) +
(buffer[i+2] << 8) +
(buffer[i+1] << 16) +
(buffer[i] << 24);
vals[count] = *reinterpret_cast<float*>(&t);
}
Python 服务器发送数据包:
packer = struct.Struct('>%sf' % 8)
packed_data = packer.pack(*values)
sock.send(packed_data)
我感觉这可能是关于 int 和 float 之间转换的问题,但我似乎无法弄清楚。非常感谢任何帮助。
buffer
正在使用char
,它可能已在您的系统上签名。如果
buffer[0]
是-1
,那么它会转换为-1
作为int
(不再是char
)。这在将所有这些字节加在一起时会导致问题(因为-1
作为char
可能是0xff
,而作为int
可能是0xffffffff
)。换句话说,如果缓冲区包含具有负值的字节,由于整数提升,这将搞砸 "merging" 字节。你在那里的重新解释打破了严格的别名规则。这很可能适用于您可能使用的任何系统,但严格来说是未定义的行为。
您可以将其替换为将字节直接分配给 float
的代码,如下所示:
int size = 32;
char buffer[size];
float vals[8];
int count = 0;
int t;
// Receive a reply from the server
if (recv(sock, buffer, size, 0) < 0) {std::cout << "Receive failed..." << std::endl;}
for (int i = 0; count < 8; i += 4, count++) {
char* ptr = (char*)(vals + count);
// switch endianness as needed (though unlikely)
// (this is effectively equivalent to std::memcpy(vals, buffer, size))
ptr[0] = buffer[0];
ptr[1] = buffer[1];
ptr[2] = buffer[2];
ptr[3] = buffer[3];
}
更好的是(为了避免不必要的内存复制),如果你知道两台机器使用相同的浮点格式和大小,你可以用 recv
:
// read the bytes directly into the floats
recv(sock, vals, size, 0);
您的代码正在使用 char
s 进行位操作,这在默认情况下对 char
进行签名的平台(大多数平台)上可能会出现问题。使用 unsigned char 更适合这种用途。
此外,您没有正确实现从套接字读取。 recv
调用不能保证获得您请求的数据量...换句话说,传递的大小只是可以读取的 maximum 数量并且有效接收的数据可能会更少。
从套接字读取时需要进行循环,在收到所有数据或结果为负数或零时退出(负数表示错误,零表示另一端已关闭端点,不再数据来了)。
char buffer[size];
...
t =
(buffer[i+3]) +
(buffer[i+2] << 8) +
(buffer[i+1] << 16) +
(buffer[i] << 24);
这不是字节交换 32 位整数的正确方法。如果 char
被签名了怎么办? (如果您使用 Windows、linux 或 OSX,就是这种情况。)
正确的方法是使用function/macrontohl
。虽然这不是 C 或 C++ 标准的一部分,但您会在任何有望在 Internet 上运行的小端系统上找到它。在 Unix 机器上,您需要 #include <arpa/inet.h>
。在 Windows,您需要 #include <winsock2.h>
.
另一种选择是不要给自己造成这种痛苦。从某种意义上说,这完全是您自己通过在 python 脚本中使用 packer = struct.Struct('>%sf' % 8)
来完成的。您显然知道目标将 运行 在小端机器上,因此将 Struct
构造函数中的 >
更改为“<”。如果您知道 python 脚本将 运行 与您的 C++ 应用程序在同一台机器上,请将 >
更改为 @
或 =
。