Gre*_*ite 22 c++ floating-point cross-platform protocol-buffers ieee-754
Google的协议缓冲区允许您在邮件中存储浮点数和双精度数.我查看了实现源代码,想知道他们是如何以跨平台的方式设法做到这一点的,我偶然发现的是:
inline uint32 WireFormatLite::EncodeFloat(float value) {
union {float f; uint32 i;};
f = value;
return i;
}
inline float WireFormatLite::DecodeFloat(uint32 value) {
union {float f; uint32 i;};
i = value;
return f;
}
inline uint64 WireFormatLite::EncodeDouble(double value) {
union {double f; uint64 i;};
f = value;
return i;
}
inline double WireFormatLite::DecodeDouble(uint64 value) {
union {double f; uint64 i;};
i = value;
return f;
}
Run Code Online (Sandbox Code Playgroud)
现在,一个重要的附加信息是这些例程不是进程的结束,而是对它们的结果进行后处理,以便以小端顺序放置字节:
inline void WireFormatLite::WriteFloatNoTag(float value,
io::CodedOutputStream* output) {
output->WriteLittleEndian32(EncodeFloat(value));
}
inline void WireFormatLite::WriteDoubleNoTag(double value,
io::CodedOutputStream* output) {
output->WriteLittleEndian64(EncodeDouble(value));
}
template <>
inline bool WireFormatLite::ReadPrimitive<float, WireFormatLite::TYPE_FLOAT>(
io::CodedInputStream* input,
float* value) {
uint32 temp;
if (!input->ReadLittleEndian32(&temp)) return false;
*value = DecodeFloat(temp);
return true;
}
template <>
inline bool WireFormatLite::ReadPrimitive<double, WireFormatLite::TYPE_DOUBLE>(
io::CodedInputStream* input,
double* value) {
uint64 temp;
if (!input->ReadLittleEndian64(&temp)) return false;
*value = DecodeDouble(temp);
return true;
}
Run Code Online (Sandbox Code Playgroud)
所以我的问题是:这在实践中确实足够好以确保C++中浮点数和双精度数的序列化可跨平台传输吗?
我在我的问题中明确地插入"实践中"这个词,因为我知道在理论上我不能对浮点数和双打在C++中实际格式化做出任何假设,但我不知道这个理论上的危险是否是实际上我应该非常担心的事情.
UPDATE
现在我认为PB可能会破坏SPARC的方法.如果我理解Oracle的这个页面正确描述了SPARC上用于数字的格式,那么SPARC使用相反的endian作为整数的x86,但是对于浮点数和双精度使用与x86 相同的endian.但是,PB首先将它们直接转换为适当大小的整数类型(通过联合;请参阅上面我的问题中引用的代码片段),然后在平台上反转字节的顺序,从而编码浮点数/双精度数. big-endian整数:
void CodedOutputStream::WriteLittleEndian64(uint64 value) {
uint8 bytes[sizeof(value)];
bool use_fast = buffer_size_ >= sizeof(value);
uint8* ptr = use_fast ? buffer_ : bytes;
WriteLittleEndian64ToArray(value, ptr);
if (use_fast) {
Advance(sizeof(value));
} else {
WriteRaw(bytes, sizeof(value));
}
}
inline uint8* CodedOutputStream::WriteLittleEndian64ToArray(uint64 value,
uint8* target) {
#if defined(PROTOBUF_LITTLE_ENDIAN)
memcpy(target, &value, sizeof(value));
#else
uint32 part0 = static_cast<uint32>(value);
uint32 part1 = static_cast<uint32>(value >> 32);
target[0] = static_cast<uint8>(part0);
target[1] = static_cast<uint8>(part0 >> 8);
target[2] = static_cast<uint8>(part0 >> 16);
target[3] = static_cast<uint8>(part0 >> 24);
target[4] = static_cast<uint8>(part1);
target[5] = static_cast<uint8>(part1 >> 8);
target[6] = static_cast<uint8>(part1 >> 16);
target[7] = static_cast<uint8>(part1 >> 24);
#endif
return target + sizeof(value);
}
Run Code Online (Sandbox Code Playgroud)
然而,这对于它在SPARC上的浮点/双精度的情况下完全是错误的,因为字节已经是"正确"的顺序.
总而言之,如果我的理解是正确的,那么浮点数不能使用PB在SPARC和x86之间传输,因为基本上PB假定所有数字都存储与给定平台上的整数具有相同的endianess(相对于其他平台),这是对SPARC做出的错误假设.
更新2
作为Lyke指出,IEEE 64位浮点点都存储在SPARC大端顺序,而相比之下,86.但是,只有两个32位字的顺序相反,而不是所有8个字节,特别是IEEE 32位浮点看起来像是按照与x86相同的顺序存储.
Jon*_*eet 10
我认为只要您的目标C++平台使用IEEE-754并且库正确处理字节序,它应该没问题.基本上你所展示的代码假设如果你有正确的顺序和IEEE-754实现,你将获得正确的价值.字节序由协议缓冲区处理,并假设IEEE-754-ness - 但非常普遍.
| 归档时间: |
|
| 查看次数: |
5104 次 |
| 最近记录: |