将双精度浮点数从小端序转换为大端序

3

我正在进行小端序和大端序之间的转换,并检查是否正确,请帮忙确认。

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>


double dx = 122992001003000;

//A method little to big endian

double bEndian(double d) {

    int *p0 = (int *) &d;
    int *p1 = p0 + 1;

    printf("1: %x %x\n", *p0, *p1);

    int tmp = *p1;
    *p1 = htonl(*p0);
    *p0 = htonl(tmp);

    printf("2: %x %x\n", *p0, *p1);

    return *(double *)p0;
}

//A method  big to little endian

double lEndian(double d) {
    int *p0 = (int *) &d;
    int *p1 = p0 + 1;

    printf("3: %x %x\n", *p0, *p1);

    int tmp = *p1;
    *p1 = ntohl(*p0);
    *p0 = ntohl(tmp);

    printf("4: %x %x\n", *p0, *p1);

    return *(double *)p0;
}

int main (int argc, char *argv[]) 
{       
    double d1 = bEndian(dx);

    double d2 = lEndian(d1);

    printf("5: %.0f\n", d2);

    if ((*(double*) &d2) == dx)
      printf("6: %.0lf\n", *(double*) &d2);
    else
      printf("7:%d\n", d2);

    return 0;
}

为什么有时候它会给我返回122992001003129这样的结果?此外,使用ntohl或htonl没有区别吗?这只是为了规范化即网络到主机和主机到网络吗?


如果您直接打印dx的值,它是正确的吗? - undefined
ntohhton只是交换字节的顺序(在大多数机器上,对于大端字节序的机器它们不起作用),所以是为了清晰起见使用不同的名称,但它们实际上是相同的。 - undefined
我怀疑你今天的问题是122992001003000.0无法在双精度格式中准确表示。 - undefined
明天,你会发现当你对一个完全普通的双精度值进行字节反转时...它会变成一个信号型非数(signalling NAN)。你真的需要返回网络字节顺序,如struct netDouble { unsigned char bytes[sizeof(double)]; }; - undefined
@Jarod42 你对严格别名规则有什么评论吗? - undefined
显示剩余7条评论
4个回答

5

这是一个可以改变字节序的函数。

#include <algorithm>
#include <cstdio>

template <class T>
T change_endian(T in)
{
    char* const p = reinterpret_cast<char*>(&in);
    for (size_t i = 0; i < sizeof(T) / 2; ++i)
        std::swap(p[i], p[sizeof(T) - i - 1]);
    return in;
}

int main()
{
    double d = 122992001003000;
    printf("%f\n", d);
    d = change_endian(d);
    printf("%f\n", d);
    d = change_endian(d);
    printf("%f\n", d);
}

输出:

122992001003000.000000
0.000000
122992001003000.000000

我认为原始解决方案没有严格别名问题。可以将任何对象视为字符数组进行检查。 - undefined
@khuttun 是的,那部分看起来像是我的问题。我会编辑答案。 - undefined

3

0
typedef union
{
    double d;
    unsigned char s[8];
} Union_t;

// reverse little to big endian or vice versa as per requirement
double reverse_endian(double in)
{
    int i, j;
    unsigned char t;
    Union_t val;

    val.d = in;
    // swap MSB with LSB etc.
    for(i=0, j=7; i < j; i++, j--)
    {
        t = val.s[i];
        val.s[i] = val.s[j];
        val.s[j] = t;
    }
    return val.d;
}

我在代码中添加了注释,以使其更易于理解。循环所做的是将第0个字节与第7个字节交换,第1个与第6个交换,依此类推。我使用了double类型的union,并与包含8个字符的数组结合使用,因为double类型占用8个字节。 - undefined

-2
简单地反转四个字节应该可以解决问题。
double ReverseDouble( const double indouble )
{
   double retVal;
   char *doubleToConvert = ( char* ) & indouble;
   char *returndouble = ( char* ) & retVal;

   // swap the bytes into a temporary buffer
   returndouble[0] = doubleToConvert[3];
   returndouble[1] = doubleToConvert[2];
   returndouble[2] = doubleToConvert[1];
   returndouble[3] = doubleToConvert[0];

   return retVal;
}

两个问题:首先,double类型占用的字节数是八个,而不是四个。其次,即使你修复了这个问题,返回double类型仍然存在问题;普通的double值可能会被转换成信号NaN值。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接