是否有必要在模板化函数中强制转换文本常量?



我正在编写一个必须以单精度和双精度编译的代码。原始版本只有双精度,但现在我正在尝试使用模板启用单精度。

我的问题是:例如,是否有必要使用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.)这样的函数样式转换),或者,您可以将此函数专门用于doublefloat

另一种不太通用但可能在这里起作用的选项是切换代码,即为单精度float编写代码,然后让编译器应用其隐式转换。由于转换通常只用于较大的类型,因此它应该适合doublefloat,而不会产生任何float开销。

当你这样做时:

return 1. + 7.8*zeta;

文字1.7.8double的,所以当zetafloat时,会先转换为double,然后整个计算会以双精度完成,结果会投射回float,这相当于:

return (float)(1. + 7.8 * (double)zeta);

否则,这相当于调用phih_stable(double)并将结果存储在float中,因此您的模板对float毫无用处。

如果您希望以单精度进行计算,则需要强制转换1

return TF(1.) + TF(7.8) * zeta;

使用1.f7.8f呢?问题是由于浮点精度(double)7.8f != 7.8。差值约为1e-77.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 doublereturn TF(1.l) + TF(7.8l) * zeta;一起使用时不会失去精度。

最新更新