我正在编写一个必须以单精度和双精度编译的代码。原始版本只有双精度,但我现在尝试使用模板启用单精度。
我的问题是:是否有必要将1.
和7.8
转换为具有static_cast<TF>(1.)
的指定类型,或者编译器会处理它吗?我发现铸件不是很漂亮,而是喜欢远离它。 (我有更长的其他函数,包含更多的文字常量)。
template<typename TF>
inline TF phih_stable(const TF zeta)
{
// Hogstrom, 1988
return 1. + 7.8*zeta;
}
答案 0 :(得分:3)
转换和隐式转换是两件事。对于此示例,您可以将模板函数视为两个重载函数,但其中包含相同的代码。在接口级别(参数,返回值),编译器将生成隐式转换。
现在,您必须问自己的问题是:那些隐含的转换是否符合我的要求?如果他们这样做,只需保持原样。如果他们不这样做,您可以尝试添加显式转换(可能使用像TF(1.)
这样的函数式转换),或者您可以将此函数专门用于double
和float
。
另一个选项,不太通用但可能在这里工作,是你切换代码,即你编写单精度float
的代码,然后让编译器应用其隐式转换。由于转化通常只会转到更大的类型,因此它应该适用于double
和float
,而不会产生float
的任何开销。
答案 1 :(得分:1)
当你这样做时:
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;
向下转换为不失精度。 < / p>