如何将大小为 64 的字节数组转换为 Arduino C++ 中的双精度值列表?

How to convert a byte array of size 64 to a list of double values in Arduino C++?

void Manager::byteArrayToDoubleArray(byte ch[]) {
  int counter = 0;
  // temp array to break the byte array into size of 8 and read it
  byte temp[64];
  // double result values
  double res[8];
  int index = 0;
  int size = (sizeof(ch) / sizeof(*ch));
  for (int i = 0; i < size; i++) {
    counter++;
    temp[i] = ch[i];
    if (counter % 8 == 0) {
      res[index] = *reinterpret_cast<double * const>(temp);
      index++;
      counter = 0;
    }
  }
}

此处 result 将是具有 count = 8.

的双精度值列表

你的问题有两点。你有一些错别字和误解。而 C++ 标准在这方面有些不完善。

我会尽力解决这两个问题。

首先,一个名为 laundry_pods 的辅助函数。它需要原始内存并将 "launders" 它放入您选择的类型的数组中,只要您选择 pod 类型:

template<class T, std::size_t N>
T* laundry_pods( void* ptr ) {
  static_assert( std::is_pod<std::remove_cv_t<T>>{} );
  char optimized_away[sizeof(T)*N];
  std::memcpy( optimized_away, ptr , sizeof(T)*N );
  T* r = ::new( ptr ) T[N];
  assert( r == ptr );
  std::memcpy( r, optimized_away, sizeof(T)*N );
  return r;
}

现在就做

void Manager::byteArrayToDoubleArray(byte ch[]) {
  double* pdouble = laundry_pods<double, 8>(ch);
}

pdouble 是指向 ch 的内存的指针,被解释为 8 个双精度数组。 (它不是它的副本,它就地解释这些字节)。

虽然 laundry_pods 似乎在复制字节,但 g++ 和 clang 都将其优化为二进制 noop。看似复制字节是一种绕过 C++ 标准中的别名限制和对象生命周期规则的方法。

它依赖于没有额外簿记开销的 pod 数组(C++ 实现可以自由执行;none 执行我所知道的。这就是非静态断言双重检查的内容),但是它 returns 指向真正诚实的双精度数组的指针。如果您想避免这种假设,您可以将每个 doulbe 创建为一个单独的对象。但是,它们不是数组,并且就标准而言,非数组上的指针算法充满了问题。

术语 "launder" 的使用与绕过别名和对象生命周期要求有关。函数 在运行时什么都不做 ,但在 C++ 抽象机中它获取内存并将其转换为二进制相同的内存,现在是一堆 doubles.

做这种 "conversion" 的诀窍是总是将 double* 转换为 char*(或 unsigned charstd::byte)。绝不会反过来。

您应该可以这样做:

void byteArrayToDoubleArray(byte* in, std::size_t n, double* out)
{
    for(auto out_bytes = (byte*) out; n--;)
        *out_bytes++ = *in++;
}

// ...

byte ch[64];

// .. fill ch with double data somehow

double res[8];

byteArrayToDoubleArray(ch, 64, res);

假设类型 bytecharunsigned charstd::byte.

的别名

由于代码 (sizeof(ch) / sizeof(*ch)) 对于未定义大小的数组没有意义,我不能完全确定您要在这里实现什么。

如果你有一个字节数组(POD 数据类型;类似于 typedef char byte;)那么这个最简单的解决方案就是 reinterpret_cast:

double *result = reinterpret_cast<double*>(ch);

这允许您使用 result[0]..result[7],只要 ch[] 有效且至少包含 64 个字节。请注意,此构造不会生成代码。它告诉编译器 result[0] 对应于 ch[0..7] 等等。访问 result[] 将导致访问 ch[].

但是你必须知道ch[]中的元素个数才能计算出result中有效双精度元素的个数。

如果你需要一个副本(因为 - 例如 - ch[] 是一个临时数组)你可以使用

std::vector<double> result(reinterpret_cast<double*>(ch), reinterpret_cast<double*>(ch) + itemsInCh * sizeof(*ch) / sizeof(double));

所以如果ch[]是一个有64项的数组,一个字节真的是一个8位的值,那么

std::vector<double> result(reinterpret_cast<double*>(ch), reinterpet_cast<double*>(ch) + 8);

将提供包含 8 个双精度值的 std::vector

还有另一种可能的联合方法:

union ByteToDouble
{
  byte b[64];
  double d[8];
} byteToDouble;

8个双精度值将占用与64字节值相同的内存。因此,您可以将字节值写入 byteToDouble.b[] 并从 byteToDouble.d[].

读取结果双精度值