我正在编写一个必须以单精度和双精度编译的代码。原始版本只有双精度,但现在我正在尝试使用模板启用单精度。
我的问题是:例如,是否有必要使用static_cast<TF>(1.)
将1.
和7.8
转换为指定类型,还是编译器会处理它?我发现演员阵容不是很漂亮,宁愿远离它。(我还有其他更长的函数,并且包含更多的文字常量)。
template<typename TF>
inline TF phih_stable(const TF zeta)
{
// Hogstrom, 1988
return 1. + 7.8*zeta;
}
强制转换和隐式转换是两件事。对于此示例,您可以将模板函数视为两个重载函数,但其中包含相同的代码。在接口级别(参数、返回值),编译器将生成隐式转换。
现在,您必须问自己的问题是:这些隐式转换是否做到了我想要的?如果他们这样做,就保持原样。如果没有,您可以尝试添加显式转换(可能使用像TF(1.)
这样的函数样式转换),或者,您可以将此函数专门用于double
和float
。
另一种不太通用但可能在这里起作用的选项是切换代码,即为单精度float
编写代码,然后让编译器应用其隐式转换。由于转换通常只用于较大的类型,因此它应该适合double
和float
,而不会产生任何float
开销。
当你这样做时:
return 1. + 7.8*zeta;
文字1.
和7.8
是double
的,所以当zeta
是float
时,会先转换为double
,然后整个计算会以双精度完成,结果会投射回float
,这相当于:
return (float)(1. + 7.8 * (double)zeta);
否则,这相当于调用phih_stable(double)
并将结果存储在float
中,因此您的模板对float
毫无用处。
如果您希望以单精度进行计算,则需要强制转换1:
return TF(1.) + TF(7.8) * zeta;
使用1.f
和7.8f
呢?问题是由于浮点精度(double)7.8f != 7.8
。差值约为1e-7
,7.8f
的实际存储值(假设32位float
)为:
7.80000019073486328125
而7.8
的实际存储值(假设 64 位double
)为:
7.79999999999999982236431605997
所以你必须问问自己,你是否接受这种精度的损失。
您可以比较以下两个实现:
template <class T>
constexpr T phih_stable_cast(T t) {
return T(1l) + T(7.8l) * t;
}
template <class T>
constexpr T phih_stable_float(T t) {
return 1.f + 7.8f * t;
}
以及以下断言:
static_assert(phih_stable_cast(3.4f) == 1. + 7.8f * 3.4f, "");
static_assert(phih_stable_cast(3.4) == 1. + 7.8 * 3.4, "");
static_assert(phih_stable_cast(3.4l) == 1. + 7.8l * 3.4l, "");
static_assert(phih_stable_float(3.4f) == 1.f + 7.8f * 3.4f, "");
static_assert(phih_stable_float(3.4) == 1. + 7.8 * 3.4, "");
static_assert(phih_stable_float(3.4l) == 1.l + 7.8l * 3.4l, "");
最后两个断言由于执行计算时精度损失而失败。
1你甚至应该从long double
向下抛弃,以便在将你的函数与long double
:return TF(1.l) + TF(7.8l) * zeta;
一起使用时不会失去精度。