更改浮点数的字节顺序的最正确方法是什么

时间:2014-11-06 11:12:34

标签: c++ endianness

阅读本文:http://commandcenter.blogspot.fi/2012/04/byte-order-fallacy.html

文章中的方法是:

从大端读取:

int i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);

从小端读取:

int i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

有没有办法将这种意识形态转换为浮点数?

有没有办法避免if(swap_needed) swap(data);

我有一个想法是从数据中单独读取符号位,尾数和指数,根据它们计算浮点值。

3 个答案:

答案 0 :(得分:4)

Sebastian Redl的答案是正确的,如果你坚持使用简单的非英特尔IEEE-754浮点数或双倍,但它将失败,英特尔的双倍和长双倍的特殊表示,以及所有其他特殊想法因为他们的长双格式。只有极少数架构使用标准的IEEE-754浮点格式。 即使是最容易使用BE / LE的最简单的mips,也有一个特殊的MIPS64 16字节长双格式。

因此没有正确而简单的方法来为浮点数做一个快速的字节换行。但是我编写了代码来从各种体系结构中读取浮点数到当前的体系结构中,这是一项艰巨的任务。 https://github.com/parrot/parrot/blob/native_pbc2/src/packfile/pf_items.c#L553 注意:英特尔专业是https://github.com/parrot/parrot/blob/native_pbc2/src/packfile/pf_items.c#L605

中用i标记的额外归一化位(尾数的最高位63)

即。我在那些,BE和LE之间进行转换:

  • Floattype 0 = IEEE-754 8字节双(binary64)
  • Floattype 1 =英特尔80位长双存储在12字节(i386)或对齐到16字节(x86_64 / ia64)
  • Floattype 2 = IEEE-754 128位四倍精度存储在16字节,Sparc64四浮点或__float128,gcc自4.3(binary128)
  • Floattype 3 = IEEE-754 4字节浮点数(binary32)
  • Floattype 4 = PowerPC 16字节双倍(-mlong-double-128)

还没有:

  • Floattype 5 = IEEE-754 2字节半精度浮点数(binary16)
  • Floattype 6 = MIPS64 16字节长双
  • Floattype 7 = AIX 16字节长双
  • CRAY和更多疯狂

由于没有大的需求,我从来没有为这个浮点转换代码创建一个合适的库。 顺便说一句。我使用更快的原生字节交换函数,请参阅https://github.com/parrot/parrot/blob/native_pbc2/include/parrot/bswap.h

通常你打印最多。精度到字符串并读取此字符串。那里你只有问题找出你的最大值。精度。

答案 1 :(得分:1)

你只需抓住底层字节并使用它。

unsigned char underlying[sizeof(float)];

// Writing
std::memcpy(underlying, &my_float, sizeof(float));
if (platform_endian != target_endian)
  std::reverse(std::begin(underlying), std::end(underlying));
write(underlying, sizeof(float));

// Reading
read(underlying, sizeof(float));
if (platform_endian != target_endian)
  std::reverse(std::begin(underlying), std::end(underlying));
std::memcpy(&my_float, underlying, sizeof(float));

如果你有这种倾向,你当然可以优化反向超级特殊的东西。

答案 2 :(得分:0)

您通常会看到人们转换为无符号的64位整数,然后调用经典的BSD函数来转换为网络字节顺序。我曾经参与过一个项目,我从Java机器上通过网络获得了double,所以我知道他们被发送到big-endian,并在C ++的Intel机器上读取它们。我只是将数据读作char[8],称为std::reverse,并将结果转换为double

double read_double()
{
    char buffer[8];
    // read from network into buffer;
    std::reverse(std::begin(buffer), std::end(buffer), std::begin(buffer));
    return *static_cast<double*>(static_cast<void*>(buffer));
}

今天我会以不同的方式做事。首先,您发布的比特转换代码并不难以理解。另一方面,我同意@NeilKirk和您链接的文章:无论机器上的实际字节顺序如何,从特定字节序读/写的代码都是相同的,所以只需编写将读取big-endian / little-endian的代码数据,使用您链接到的文章中的代码(在您读取并将字节操作为64位无符号整数类型后,将其强制转换为double)。