数据转换:将数组中的 4 字节数据分配给 1 字节数组

Data conversion: Assigning 4 byte data from array into 1 byte array

首先,让我稍微解释一下环境。我正在为嵌入式 32 位微控制器使用 C。通过各种工具进行单元测试,但结果是一样的。 printf 仅用于 MinGW 的测试。

我正在尝试将数据从 float32(4 字节数组)(IEEE754) 数组复制到字节数组中。

我在这里只使用十六进制数据,重要的是十六进制值的位置是准确的,即:

If CalibData[0] = 01 02 03 04

那么数据应该是这样的:

   Data[0] = 01
   Data[1] = 02
   Data[2] = 03
   Data[3] = 04

我能想到的可能性类似于下面提到的一个代码块:

/*CalibData is a Global variable and change by other components, I am only reading it here*/
float32 CalibData [1920];

/*Data is an argument for my function and shall return it with values read from CalibData*/
int Data[7680];

/* Approach 1: */

uint16_t dataElementCounter = 0;
for (uint16_t i = 0; i < 1920; i++)
{
Data[dataElementCounter] = (uint8) ((uint32) calib_Data[i] >> 24);
dataElementCounter++;
Data[dataElementCounter] = (uint8) ((uint32) calib_Data[i] >> 16);
dataElementCounter++;
Data[dataElementCounter] = (uint8) ((uint32) calib_Data[i] >> 8);
dataElementCounter++;
Data[dataElementCounter] = (uint8) ((uint32)calib_Data[i]);
dataElementCounter++;
}

/* Approach 2: */
for (uint16_t i = 0; i < 1920; i++)
{
memcpy((uint8*) Data[0], &calib_Data[i],sizeof(float));
}

现在的问题是,如果我使用方法 1,那么在没有硬件的情况下很难测试数据是否已正确复制,因为通过单元测试或 printf 测试它总是提供截断的数据。例如

if CalibData[0] = 1.0;

根据 IEEE754,它应该是十六进制的 0x3F 0x80 0x00 0x00,但在单元测试或 printf 中它会像这样:

CalibData >> 24 是分数 1.0 / 16777216。当转换为 uint32 时,它会截断为 0。 CalibData >> 16 是分数 1.0 / 65536。同样。 CalibData >> 8 是分数 1.0 / 256。同样。 校准数据为 1.0。这将成为 1 作为 uint32。

因此数据将显示为:

Data[0] = 0
Data[1] = 0
Data[2] = 0
Data[3] = 1

如果我使用的是方法 2,则代码会在数据中显示垃圾值。

请告诉我更好的方法或改进上述方法之一。

此致

错误是转换 (uint32),因为它将 float 转换为 uint32,这不是您想要的。

你最好的选择是这段摘录:

union {
    float f;
    uint8 b[4];
} u;

u.f = calib_Data[i];
Data[dataElementCounter] = u.b[0];
dataElementCounter++;
Data[dataElementCounter] = u.b[1];
dataElementCounter++;
Data[dataElementCounter] = u.b[2];
dataElementCounter++;
Data[dataElementCounter] = u.b[3];
dataElementCounter++;

注意,这不符合 C 标准。但它可能适用于您的实际系统。

编辑 1

仍然不符合要求,但没有使用 union,您可以使用强制转换的指针来模拟它:

const uint8* b = (const uint8*)(calib_Data + i); // equally, but simpler to read than &calib_Data[i]
Data[dataElementCounter] = b[0];
dataElementCounter++;
Data[dataElementCounter] = b[1];
dataElementCounter++;
Data[dataElementCounter] = b[2];
dataElementCounter++;
Data[dataElementCounter] = b[3];
dataElementCounter++;

这类似于 codetest 的答案,它甚至更好地建议使用 memcpy():

memcpy(Data + dataElementCounter, calib_Data + i, sizeof calib_Data[i]);
dataElementCounter += sizeof calib_Data[i];

最后一个选项有两个主要优点:

  1. 它是预先可以理解的,行数越少越好。
  2. calib_Data 的数据类型的大小变化是安全的。

编辑 2

要求:您必须交换字节。

如果您的目标系统是固定的,并且您完全确定自己在做什么,那么要做的第一件事就是在评论中对此进行记录。此外,您可能想检查编译器:

 #if !defined(MY_COMPILER_ID)
 #error Compiler not supported
 #endif

要以相反的顺序存储浮点数,您可以使用带倒排索引的 EDIT 1 的第一个替代方案:

const uint8* b = (const uint8*)(calib_Data + i); // equally, but simpler to read than &calib_Data[i]
Data[dataElementCounter] = b[3];
dataElementCounter++;
Data[dataElementCounter] = b[2];
dataElementCounter++;
Data[dataElementCounter] = b[1];
dataElementCounter++;
Data[dataElementCounter] = b[0];
dataElementCounter++;

为了使其在 calib_Data 的数据类型大小发生变化时保持安全,您需要一个循环:

const uint8* b = (const uint8*)(calib_Data + i); // equally, but simpler to read than &calib_Data[i]
int b_i;
for (b_i = sizeof calib_Data[i] - 1; b_i >= 0; --b_i) {
    Data[dataElementCounter] = b[b_i];
    dataElementCounter++;
}