在C中将字节数组转换为float / long的问题

时间:2010-08-29 22:32:13

标签: c floating-point long-integer

长:

  char long_num[8];
  for(i=0; i<8; i++)
    long_num[i] = data[pos++];

  memcpy(&res, long_num, 8);

long_num中的值如下:

127 -1 -1 -1 -1 -1 -1 -1

res应该是签名long的最大值,而是-129。

编辑:这个是照顾的。这是通信问题的结果:对于提供data的人,long是八个字节;对于我的C,这是四个。

浮子:

  float *res;
  /* ... */
  char float_num[4];
  for(i=0; i<4; i++)
    float_num[i] = data[pos++];

  res = (float *)float_num;

它是零。数组值:

62 -1 24 50

我也尝试了memcpy(),但它也产生了零。我做错了什么?


我的系统:Linux 2.6.31-16-generic i686 GNU/Linux

4 个答案:

答案 0 :(得分:3)

您正在little-endian系统上运行代码。反转数组中的字节顺序,然后重试:

signed char long_num[] = {-1, -1, -1, -1, -1, -1, -1, 127};
// ...

答案 1 :(得分:2)

这是两个完全无关的问题。

在第一个中,您的计算机是小端的。符号位在您拼凑的long中设置,因此结果为负。它接近零,因为设置了许多“最重要的位”。

在第二个例子中,strict aliasing rules的不尊重可能是对奇怪行为的解释。我不确定。如果您正在使用gcc,请尝试使用union,gcc保证使用union以这种方式转换数据时会发生什么。

答案 2 :(得分:1)

鉴于此代码:

#include <stdio.h>
#include <string.h>

int main(void)
{
    {
        long res;
        char long_num[8] = { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
        memcpy(&res, long_num, 8);
        printf("%ld = 0x%lX\n", res, res);
    }
    {
        float res;

        char float_num[4] = { 62, 0xFF, 24, 50 };
        memcpy(&res, float_num, 4);
        printf("%f = %19.14e\n", res, res);

    }
    return 0;
}

使用GCC 4.5.1在MacOS X 10.6.4上以64位模式进行编译:

-129 = 0xFFFFFFFFFFFFFF7F
0.000000 = 8.90559981314709e-09

对于小端英特尔机器来说这是正确的(好吧,'长'值是正确的。)

你要做的是有点不寻常 - 不推荐。它不可移植,尤其是因为有关于endian-ness的问题。

我之前在SPARC机器上写了一些相关的代码(这是一个大端机器):

union u_double
{
    double  dbl;
    char    data[sizeof(double)];
};

union u_float
{
    float   flt;
    char    data[sizeof(float)];
};

static void dump_float(union u_float f)
{
    int exp;
    long mant;

    printf("32-bit float: sign: %d, ", (f.data[0] & 0x80) >> 7);
    exp = ((f.data[0] & 0x7F) << 1) | ((f.data[1] & 0x80) >> 7);
    printf("expt: %4d (unbiassed %5d), ", exp, exp - 127);
    mant = ((((f.data[1] & 0x7F) << 8) | (f.data[2] & 0xFF)) << 8) | (f.data[3] & 0xFF);
    printf("mant: %16ld (0x%06lX)\n", mant, mant);
}

static void dump_double(union u_double d)
{
    int exp;
    long long mant;

    printf("64-bit float: sign: %d, ", (d.data[0] & 0x80) >> 7);
    exp = ((d.data[0] & 0x7F) << 4) | ((d.data[1] & 0xF0) >> 4);
    printf("expt: %4d (unbiassed %5d), ", exp, exp - 1023);
    mant = ((((d.data[1] & 0x0F) << 8) | (d.data[2] & 0xFF)) << 8) | (d.data[3] & 0xFF);
    mant = (mant << 32) | ((((((d.data[4] & 0xFF) << 8) | (d.data[5] & 0xFF)) << 8) | (d.data[6] & 0xFF)) << 8) | (d.data[7] & 0xFF);
    printf("mant: %16lld (0x%013llX)\n", mant, mant);
}

static void print_value(double v)
{
    union u_double d;
    union u_float  f;

    f.flt = v;
    d.dbl = v;

    printf("SPARC: float/double of %g\n", v);
    image_print(stdout, 0, f.data, sizeof(f.data));
    image_print(stdout, 0, d.data, sizeof(d.data));
    dump_float(f);
    dump_double(d);
}


int main(void)
{
    print_value(+1.0);
    print_value(+2.0);
    print_value(+3.0);
    print_value( 0.0);
    print_value(-3.0);
    print_value(+3.1415926535897932);
    print_value(+1e126);
    return(0);
}

这是我在那个平台上得到的。请注意,尾数中有一个隐含的“1”位,因此“3”的值只设置了一个位,因为暗示了另一个1位。

SPARC: float/double of 1
0x0000: 3F 80 00 00                                       ?...
0x0000: 3F F0 00 00 00 00 00 00                           ?.......
32-bit float: sign: 0, expt:  127 (unbiassed     0), mant:                0 (0x000000)
64-bit float: sign: 0, expt: 1023 (unbiassed     0), mant:                0 (0x0000000000000)
SPARC: float/double of 2
0x0000: 40 00 00 00                                       @...
0x0000: 40 00 00 00 00 00 00 00                           @.......
32-bit float: sign: 0, expt:  128 (unbiassed     1), mant:                0 (0x000000)
64-bit float: sign: 0, expt: 1024 (unbiassed     1), mant:                0 (0x0000000000000)
SPARC: float/double of 3
0x0000: 40 40 00 00                                       @@..
0x0000: 40 08 00 00 00 00 00 00                           @.......
32-bit float: sign: 0, expt:  128 (unbiassed     1), mant:          4194304 (0x400000)
64-bit float: sign: 0, expt: 1024 (unbiassed     1), mant: 2251799813685248 (0x8000000000000)
SPARC: float/double of 0
0x0000: 00 00 00 00                                       ....
0x0000: 00 00 00 00 00 00 00 00                           ........
32-bit float: sign: 0, expt:    0 (unbiassed  -127), mant:                0 (0x000000)
64-bit float: sign: 0, expt:    0 (unbiassed -1023), mant:                0 (0x0000000000000)
SPARC: float/double of -3
0x0000: C0 40 00 00                                       .@..
0x0000: C0 08 00 00 00 00 00 00                           ........
32-bit float: sign: 1, expt:  128 (unbiassed     1), mant:          4194304 (0x400000)
64-bit float: sign: 1, expt: 1024 (unbiassed     1), mant: 2251799813685248 (0x8000000000000)
SPARC: float/double of 3.14159
0x0000: 40 49 0F DB                                       @I..
0x0000: 40 09 21 FB 54 44 2D 18                           @.!.TD-.
32-bit float: sign: 0, expt:  128 (unbiassed     1), mant:          4788187 (0x490FDB)
64-bit float: sign: 0, expt: 1024 (unbiassed     1), mant: 2570638124657944 (0x921FB54442D18)
SPARC: float/double of 1e+126
0x0000: 7F 80 00 00                                       ....
0x0000: 5A 17 A2 EC C4 14 A0 3F                           Z......?
32-bit float: sign: 0, expt:  255 (unbiassed   128), mant:                0 (0x000000)
64-bit float: sign: 0, expt: 1441 (unbiassed   418), mant:      -1005281217 (0xFFFFFFFFC414A03F)

你必须对代码做一些努力才能让它在像英特尔机器这样的小端机器上运行良好。

答案 3 :(得分:0)

如果您通过网络在不同计算机之间进行通信(如更新所示),则必须定义协议以确保两端都知道如何准确地将数据传输到另一端。它不一定是微不足道的 - 世界上有许多复杂的系统。

  • 一种标准方法是定义字节的规范排序 - 以及类型的规范大小。例如,在处理IPv4地址时,这通常称为“网络字节顺序”。它部分地定义了数据的字节序;它也是关于定义值作为4字节值而不是8字节值发送 - 反之亦然。

  • 另一种技术基于ASN.1 - 它使用类型,长度和值(TLV编码)对数据进行编码。发送的每一位数据都带有标识正在发送内容的信息。

  • IBM DB2 DBMS使用的DRDA协议具有不同的策略 - “接收者正确”。发送者在会话开始时以某种方式识别他们是什么类型的机器,然后以他们自己最方便的格式发送数据。接收方负责修复发送的内容。 (这适用于DB服务器和数据库客户端;客户端以其首选表示法发送,服务器修复它接收的内容,而服务器以其首选表示法发送,客户端修复它收到的内容。)

    < / LI>
  • 处理问题的另一种非常有效的方法是使用文本协议。数据作为数据的文本版本传输,具有用于识别不同字段的明确机制。这比各种二进制编码机制更容易调试,因为您可以转储数据并查看发生了什么。它的效率不一定低于二进制协议 - 特别是如果您通常发送实际包含单位数整数值的8字节整数。