如何将大小为 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++ 抽象机中它获取内存并将其转换为二进制相同的内存,现在是一堆 double
s.
做这种 "conversion" 的诀窍是总是将 double*
转换为 char*
(或 unsigned char
或 std::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);
假设类型 byte
是 char
或 unsigned char
或 std::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[]
.
读取结果双精度值
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++ 抽象机中它获取内存并将其转换为二进制相同的内存,现在是一堆 double
s.
做这种 "conversion" 的诀窍是总是将 double*
转换为 char*
(或 unsigned char
或 std::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);
假设类型 byte
是 char
或 unsigned char
或 std::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[]
.