如何改变双重字节序?

时间:2016-04-08 11:12:26

标签: c endianness

我需要从串口读取数据。它们是小端的,但我需要独立于平台,所以我必须覆盖double的字节序。我无法找到任何地方,怎么做,所以我写了自己的功能。但我不确定。 (而且我没有一台带有大端的机器来试试它。)

这会正常吗?还是有一些我无法找到的更好的方法?

double get_double(uint8_t * buff){
    double value;
    memcpy(&value,buff,sizeof(double));
    uint64_t tmp;
    tmp = le64toh(*(uint64_t*)&value);
    value = *(double*) &tmp;
    return value;
}

P.S。我估计double长8个字节,所以不要为此烦恼。我知道这个可能有问题

编辑:经过建议,我应该使用工会,我这样做了:

union double_int{
    double d;
    uint64_t i;
};


double get_double(uint8_t * buff){
    union double_int value;
    memcpy(&value,buff,sizeof(double));
    value.i = le64toh(value.i);
    return value.d;
}

更好? (虽然我没有看到太多差异)

EDIT2:attemtp#3,你现在怎么想?

double get_double(uint8_t * buff){
    double value;
    uint64_t tmp;
    memcpy(&tmp,buff,sizeof(double));
    tmp = le64toh(tmp);
    memcpy(&value,&tmp,sizeof(double));
    return value;
}

Edit3:我用gcc -std=gnu99 -lpthread -Wall -pedantic

编译它

Edit4:在下一个建议之后,我添加了一个用于字节顺序检查的条件。我老实说不知道,我现在在做什么(不应该有类似__DOUBLE_WORD_ORDER__的东西吗?)

double get_double(uint8_t * buff){
    double value;
    if (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__){
        uint64_t tmp;
        memcpy(&tmp,buff,sizeof(double));
        tmp = le64toh(tmp);
        memcpy(&value,&tmp,sizeof(double));
    }
    else {
        memcpy(&value,buff,sizeof(double));
    }
    return value;
}

3 个答案:

答案 0 :(得分:2)

我只是去手动将字节复制到临时双,然后返回。在C(我认为是C ++)中,可以将任何指针转换为char *;这是别名的显式权限之一(在标准草案n1570,参数6.5 / 7中),正如我在其中一篇评论中提到的那样。为了完成接近硬件的任何事情,这个例外是绝对必要的;包括通过网络收到的反转字节: - )。

没有标准的编译时间方法来确定这是否是必要的,这是可惜的;如果你想避免分支,如果你处理大量数据可能是一个好主意,你应该查找编译器的专有定义文档,以便你可以在编译时选择正确的代码分支。 gcc, for example,已将__FLOAT_WORD_ORDER__设置为__ORDER_LITTLE_ENDIAN____ORDER_BIG_ENDIAN__

(由于你在评论中的问题:__FLOAT_WORD_ORDER__一般意味着浮点。设计一个FPU,对不同的数据大小有不同的字节顺序,这将是一个非常恶心的头脑:-)。在所有现实中,没有许多主流架构具有浮点与整数类型的不同字节顺序。正如维基百科所说,小型系统可能会有所不同。)

Basile指向ntohd,这是一种转换功能,存在于Windows上,但显然不在Linux上。

我的天真样本实现就像

/** copy the bytes at data into a double, reversing the
    byte order, and return that.
*/
double reverseValue(const char *data)
{
    double result;

    char *dest = (char *)&result;

    for(int i=0; i<sizeof(double); i++) 
    {
        dest[i] = data[sizeof(double)-i-1];
    }
    return result;
}

/** Adjust the byte order from network to host.
    On a big endian machine this is a NOP.
*/
double ntohd(double src)
{
#   if      !defined(__FLOAT_WORD_ORDER__) \
        ||  !defined(__ORDER_LITTLE_ENDIAN__)
#       error "oops: unknown byte order"
#   endif

#   if __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__
        return reverseValue((char *)&src);
#   else
        return src;
#   endif
}

这里有一个有效的例子:https://ideone.com/aV9mj4

改进的版本可以满足给定的CPU - 它可能有一个8字节的交换命令。

答案 1 :(得分:1)

如果您可以调整软件的两面(发射器和接收器),您可以使用一些serialization库和格式。它可以在您的案例中使用旧的XDR例程,例如xdr_double(请参阅xdr(3) ...)。您还可以考虑ASN1或使用JSON等文字格式。

XDR是big endian。你可能试图找到一些NDR实现,是小端。

另请参阅this相关问题,以及htond

的STFW

答案 2 :(得分:1)

好的,最后,感谢大家,我找到了最好的解决方案。现在我的代码看起来像这样:

double reverseDouble(const char *data){
    double result;
    char *dest = (char *)&result;
    for(int i=0; i<sizeof(double); i++) 
        dest[i] = data[sizeof(double)-i-1];
    return result;
}

double get_double(uint8_t * buff){
    double value;
    memcpy(&value,buff,sizeof(double));

    if (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__)
        return reverseDouble((char *)&value);
    else
        return value;
}

P.S。 (检查定义等是在其他地方)