繁体   English   中英

编译时(constexpr)float modulo?

[英]Compile-time (constexpr) float modulo?

考虑以下函数,它在编译时根据参数类型计算积分或浮点模数:

template<typename T>
constexpr T modulo(const T x, const T y)
{
    return (std::is_floating_point<T>::value) ? (x < T() ? T(-1) : T(1))*((x < T() ? -x : x)-static_cast<long long int>((x/y < T() ? -x/y : x/y))*(y < T() ? -y : y))
    : (static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(x)
      %static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(y));
}

这个功能的身体能改善吗? (我需要为整数和浮点类型都有一个函数)。

这是清理它的一种方法:

#include <type_traits>
#include <cmath>

template <typename T>  //     integral?       floating point?
bool remainder_impl(T a, T b, std::true_type, std::false_type) constexpr
{
    return a % b;  // or whatever
}

template <typename T>  //     integral?        floating point?
bool remainder_impl(T a, T b, std::false_type, std::true_type) constexpr
{
    return std::fmod(a, b); // or substitute your own expression
}

template <typename T>
bool remainder(T a, T b) constexpr
{
    return remainder_impl<T>(a, b,
             std::is_integral<T>(), std::is_floating_point<T>());
}

如果您尝试在非算术类型上调用此函数,则会出现编译器错误。

我宁愿这样定义它(模板别名+模板重载):

#include <type_traits>

using namespace std;

// For floating point types

template<typename T, typename enable_if<is_floating_point<T>::value>::type* p = nullptr>
constexpr T modulo(const T x, const T y)
{
    return (x < T() ? T(-1) : T(1)) * (
            (x < T() ? -x : x) -
            static_cast<long long int>((x/y < T() ? -x/y : x/y)) * (y < T() ? -y : y)
            );
}

// For non-floating point types

template<typename T>
using TypeToCast = typename conditional<is_floating_point<T>::value, int, T>::type;

template<typename T, typename enable_if<!is_floating_point<T>::value>::type* p = nullptr>
constexpr T modulo(const T x, const T y)
{
    return (static_cast<TypeToCast<T>>(x) % static_cast<TypeToCast<T>>(y));
}

int main()
{
    constexpr int x = modulo(7.0, 3.0);
    static_assert((x == 1.0), "Error!");
    return 0;
}

它更长,但更清洁IMO。 我假设“单一功能”是指“可以统一调用的东西”。 如果你的意思是“单个功能模板 ”,那么我只是保持模板别名改进并保持过载。 但是,正如另一个答案中所提到的,为什么你需要有一个单一的功能模板并不清楚。

你问,

“这个功能的身体能改善吗?”

当然。 现在它是一个意大利面条混乱:

template<typename T>
constexpr T modulo(const T x, const T y)
{
    return (std::is_floating_point<T>::value) ? (x < T() ? T(-1) : T(1))*((x < T() ? -x : x)-static_cast<long long int>((x/y < T() ? -x/y : x/y))*(y < T() ? -y : y))
    : (static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(x)
      %static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(y));
}

你澄清一下......

“(我需要为整数和浮点类型都有一个函数)”

那么模板不是一个单一的功能。 这是一个模板。 从中生成函数。

这意味着您的问题建立在错误的假设之上。

删除该假设后,简化函数体的一种方法是将浮点类型与其他数值类型相比较,这应该是理所当然的。 为此,将函数模板实现放在一个类中(因为C ++不支持函数的部分特化,只支持类的特殊化)。

然后你可以使用各种格式化技巧 ,包括“0?0:blah”技巧,使函数更具可读性,包括线条和缩进等内容! :-)


附录 :深入研究你的代码我看到你随意抛弃long intint而忽略了调用者的类型。 那是不好的。 编写一堆自动化测试用例,调用具有各种参数类型和大/小值的函数可能是个好主意。

template <class T>
constexpr
T
modulo(T x, T y)
{
    typedef typename std::conditional<std::is_floating_point<T>::value,
                                        int,
                                        T
                                     >::type Int;
    return std::is_floating_point<T>() ?
              x - static_cast<long long>(x / y) * y :
              static_cast<Int>(x) % static_cast<Int>(y);
}

我相信有更简单的方法:

// Special available `%`
template <typename T, typename U>
constexpr auto modulo(T const& x, U const& y) -> decltype(x % y) {
    return x % y;
}

注意:基于%检测,因此只要它们实现运算符,也适用于自定义类型。 当我在它的时候,我也把它变成了混合型。

// Special floating point
inline constexpr float modulo(float x, float y) { return /*something*/; }

inline constexpr double modulo(double x, double y) { return /*something*/; }

inline constexpr long double modulo(long double x, long double y) { return /*something*/; }

注意:让fmod可用更清楚,不幸的是我不相信它是constexpr ; 因此我选择对浮点类型使用非模板模数,这允许您执行魔术来计算可能基于类型的二进制表示的精确模数。

如果你愿意,你可以做得更简单:

template<typename A, typename B>
constexpr auto Modulo(const A& a, const B& b) -> decltype(a - (b * int(a/b)))
{
    return a - (b * int(a/b));
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM