【发布时间】:2023-03-16 17:25:01
【问题描述】:
是否可以安全地将双精度数组 memcpy 到浮点数组?
【问题讨论】:
-
取决于您所说的安全。
是否可以安全地将双精度数组 memcpy 到浮点数组?
【问题讨论】:
取决于你想要什么。这些值肯定不会被保留。如果需要,请使用std::copy。
#include <algorithm>
int main()
{
double a[] = {1.618, 3.1416, 2.7, 0.707, 1.0};
float b[5];
std::copy(a, a + 5, b);
}
【讨论】:
std::copy 从双精度到浮点的隐式精度损失。我不得不使用 std::transform 函数,使用 lamda。这也更清楚地表明实际上正在发生显式的精度损失。
问题在于无法保证编译器对double 的二进制表示是float 的等效表示。为了将memcpy 用于多字节类型,底层表示必须相同(相同的布局)。您可以安全地将float 复制到float、int 到int 和double 到double。
当源类型与目标类型不匹配时,您注定未定义的行为,例如从long 复制到char 或float 到double。 memcpy 函数不进行任何转换或执行任何促销。它只是复制。
【讨论】:
就像许多其他人回答的那样,使用memcpy 不起作用,因为这两种类型(通常)大小不同。请在http://en.cppreference.com/w/cpp/language/types 上查看更多信息,或者更具体地说:
浮点类型
float - 单精度浮点类型。 通常为 IEEE-754 32 位浮点型
double - 双精度浮点类型。通常为 IEEE-754 64 位浮点型
long double - 扩展精度浮点类型。不一定映射到 IEEE-754 规定的类型。通常在 x86 和 x86-64 架构上使用 80 位 x87 浮点类型。
使用 std::copy 会给你一个编译器警告(至少对我来说是 VS2015/VS2017 编译器),因为编译器不允许通过 std::copy 从 double 到 float 隐式损失精度,而不警告开发人员关于它。如果你设置了treat warnings as errors 标志,你会得到一个编译器错误。
1>C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xutility(2316): error C2220: warning treated as error - no 'object' file generated
1>C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xutility(2335): note: see reference to function template instantiation '_OutIt std::_Copy_unchecked1<_InIt,_OutIt>(_InIt,_InIt,_OutIt,std::_General_ptr_iterator_tag)' being compiled
1> with
1> [
1> _OutIt=float *,
1> _InIt=double *
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xutility(2354): note: see reference to function template instantiation '_OutIt *std::_Copy_unchecked<_InIt,float*>(_InIt,_InIt,_OutIt)' being compiled
1> with
1> [
1> _OutIt=float *,
1> _InIt=double *
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xutility(2364): note: see reference to function template instantiation '_OutIt std::_Copy_no_deprecate1<double*,_OutIt>(_InIt,_InIt,_OutIt,std::random_access_iterator_tag,std::random_access_iterator_tag)' being compiled
1> with
1> [
1> _OutIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<float>>>,
1> _InIt=double *
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xutility(2373): note: see reference to function template instantiation '_OutIt std::_Copy_no_deprecate<_InIt,_OutIt>(_InIt,_InIt,_OutIt)' being compiled
1> with
1> [
1> _OutIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<float>>>,
1> _InIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<double>>>
1> ]
1>test.cpp(153): note: see reference to function template instantiation '_OutIt std::copy<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<double>>>,std::_Vector_iterator<std::_Vector_val<std::_Simple_types<float>>>>(_InIt,_InIt,_OutIt)' being compiled
1> with
1> [
1> _OutIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<float>>>,
1> _InIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<double>>>
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xutility(2316): warning C4244: '=': conversion from 'double' to 'float', possible loss of data
相反,我建议使用 std::transform 函数,并结合执行特定演员阵容的 lamda。这也更清楚地表明实际上正在发生显式的精度损失。
std::vector<double> doubles = { 5.0, 10.0, 242.130, 42.0 };
std::vector<float> floats(doubles.size());
std::transform(std::begin(doubles), std::end(doubles), std::begin(floats), [&](const double& value) { return static_cast<float>(value); });
【讨论】:
transform 有效,更明确,因此在某些代码库中可能是首选,但 copy 的“无效”显然是错误的。
一般情况下 - 不。
在特定情况下,在给定平台上,float 和 double 的表示可能相同,并且复制会成功。但无论如何它没有任何实际意义。
【讨论】:
memcpy 与类型无关(只看到字节)并且不能进行类型转换。正如@AzP 所说,只需使用std::transform:
std::transform(a, a + 5, b, [](double d) -> float {return float(d);});
【讨论】: