如何在模板中定义浮点常量.避免在运行时强制转换

How to define floating point constants within template. Avoid casts at run-time

本文关键字:运行时 转换 定义 浮点常量      更新时间:2023-10-16

假设我有一个简单的函数可以做这样的事情:

template<typename T>
T get_half(T a){
    return 0.5*a;
}

该函数通常将在T为双或浮点的情况下进行评估。标准规定0.5将是一个双精度(0.5f表示浮动)。如何编写上面的代码,使0.5始终是T类型,以便在评估产品或退货时没有强制转换?我希望0.5在编译时是一个T类型的常数。这个问题的重点是,我希望避免在运行时进行转换。

例如,如果我写:

template<typename T>
T get_half(T a){
    return T(0.5)*a;
}

我能绝对确定T(0.5)是在编译时评估的吗?如果没有,实现这一目标的正确方法是什么?如果需要的话,我可以使用c++11。

提前谢谢。

在c++11中,我有一个numeric_traits类,如下所示(在头文件中)

template<typename Scalar>
struct numeric_traits{
    static constexpr Scalar one_half = 0.5;
    //Many other useful constants ....
};

所以在我的代码中,我会使用这个作为:

template<typename T>
T get_half(T a){
    return numeric_traits<T>::one_half*a;
}

这就是我想要的,即0.5在编译时以我需要的精度解析,并且在运行时不会发生强制转换。然而,缺点是:

  • 每次需要新常量时,我都需要修改numeric_traits
  • sintax可能太烦人了?(当然,这不是什么大问题)
  • 最好是有这样的东西:常量(0.5),它在运行时解析为T类型

再次提前感谢您。

没有也不可能有任何方法强制在运行时永远不计算常量,因为有些机器根本没有一条指令可以加载一种类型的所有可能值。例如,机器可能只有16位加载常量指令,其中0x12345678需要在运行时计算为0x1234 << 16 | 0x5678。或者,这样一个常数可以从内存中加载,但这可能是一个比计算它更昂贵的操作

你需要稍微相信你的编译器。在可行的系统上,任何具有任何优化量的编译器都将以翻译0.5f的方式翻译T(0.5),假设Tfloat0.5f将以对您的平台最合理的方式进行计算。这可能涉及将其加载为常量,也可能涉及计算它。或者谁知道呢,如果得到相同的结果,编译器可能会将T(0.5)*a更改为a/2

在您的问题中,您将给出一个添加numeric_traits辅助类的示例。国际海事组织认为,这太过分了。在constexpr产生影响的极不可能的情况下,您可以只写

template <typename T>
T get_half(T a) {
    constexpr T half = 0.5;
    return half * a;
}

然而,在我看来,这仍然弊大于利:您的get_half现在不能再与非文字类型一起使用。它需要类型来支持常量表达式中double的转换。假设您有一个任意精度的rational类型,编写时没有考虑constexpr。现在不能使用get_half,因为初始化constexpr T half = 0.5;无效,即使0.5 * a可能已经编译。

即使使用numeric_traits帮助程序类,情况也是如此;它并不是因为我把它移到了函数体中而无效。