【问题标题】:pow for Integral Valuespow 积分价值观
【发布时间】:2017-11-18 03:44:31
【问题描述】:

我需要一个用于整数的pow 版本。我有 2 个问题需要通过 pow 解决:

  1. 如果结果大于我的整数类型,我需要钳位到numeric_limits::max()
  2. 我需要能够处理 41.99999 向上取整到 42,而不是向下取整到 41

C++ 是否在这里为我提供了某种内联解决方案,还是我一直在编写自己的函数:

template <typename T>
enable_if_t<is_integral_v<T>, T> mypow(const T base, unsigned int exp) {
    T result = exp == 0U ? base : 1;

    while(exp-- > 1U) {
        if(numeric_limits<T>::max() / result <= base) return numeric_limits<T>::max();
        result *= base;
    }
    return result;
}

【问题讨论】:

  • 您不能使用std::pow 并在需要时截断结果吗?
  • @NathanOliver 是的......如果有办法保证 12 不会被违反。有没有办法做到这一点?
  • 您可以使用尾递归和一些数字技巧来提高运行时性能。此外,您可以将此方法设为 constexpr。 stackoverflow.com/questions/9348933/…

标签: c++ floating-point integer pow clamp


【解决方案1】:

你的代码没有编译,如果可能的话,你应该先检查你的代码编译,使用你的编译器,或者先在编译器资源管理器上检查它。

另外,您忘记考虑负值。这是整体权力的一个非常重要的特征。下面的代码适用于常规 int 类型。我将让您探索如何将它扩展到其他整数类型。

#include <type_traits>
#include <iostream>
#include <cmath>
#include <limits>

using namespace std;

template <typename T>
enable_if_t< is_integral<T>::value, T> 
mypow(T base, unsigned int exp) 
{
    T result = T(1);
    bool sign = (base < 0);

    if (sign) base = -base;

    T temp = result;
    while(exp-- != 0) 
    {
        temp *= base;
        if (temp < result)
        {
            return (sign) ? numeric_limits<T>::min() 
                          : numeric_limits<T>::max();
        }
        result = temp;
    }
    return (sign && (exp & 1)) ? -result : result;
}

template <typename T>
enable_if_t< !is_integral<T>::value, int> 
mypow(const T& base, unsigned int exp) 
{
    T result = T(1);
    int i_base = int(floor(base + .5));
    bool sign = (i_base < 0);

    if (sign) i_base = -i_base;

    int temp = result;
    while(exp-- != 0) 
    {
        temp *= i_base;
        if (temp < result)
        {
            return (sign) ? numeric_limits<int>::min() : numeric_limits<int>::max();
        }
        result = temp;
    }
    return (sign && (exp & 1)) ? -result : result;
}

在现实生活中,我会注意地板的使用,即使是在积分情况下。

  template<typename T>
   enable_if_t< is_integral<T>::value, T> 
   mypow(T x, unsigned int y) { return T(floor(pow(x, y) + .5)); }

   template<typename T>
   enable_if_t< !is_integral<T>::value, int> 
   mypow(T x, unsigned int y) { return int(floor(pow(floor(x + .5), y) + .5)); }

【讨论】:

  • 我只是想以此为例,但是,这是我不想编写自己的函数的另一个原因:(
  • 在支持浮点运算的 CPU 上使用浮点 pow 通常会更快。一些现代 CPU 具有浮点 pow 指令。
  • @JonathanMee:也许你应该看看这个关于这个主题的有趣讨论:stackoverflow.com/questions/101439/…
  • 如果x is_integral,那么你为什么要使用floor(x+.5) 而不是double(x)?您是否担心无法表示为 double 的 64 位整数?反正他们搞砸了你的代码。
  • @Walter 抱歉,这是第二个函数中的拼写错误,用于浮点数。我正在四舍五入该值,因为这是原始问题的一部分。感谢您指出这一点..
【解决方案2】:

C++ 是否在这里为我提供了某种内联解决方案

不,标准库中没有整数pow

还是我一直在编写自己的函数

是的,您可以编写自己的函数。请注意,您显示的乘法循环可能比使用 std::pow 来实现该函数要慢,尤其是因为您在循环中还有一个分支和除法:

template<class I>
I int_pow_no_overflow(I base, I exp)
{
    double max = std::numeric_limits<I>::max();
    double result = std::round(std::pow(base, exp));
    return result >= max
        ? max
        : result;
}

对于更通用的方法,您可能还需要考虑下溢。

还有其他更快的算法(例如平方求幂)用于整数求幂而不是你展示的线性算法,但我不确定是否值得考虑它们,除非你处理任意精度算术,或没有浮点单元的嵌入式系统。

【讨论】:

  • 如果您有更好的方法来避免截断并使用pow 进行钳制,我会全力以赴。我所拥有的是我能想到的最好的,而且非常昂贵。
  • @JonathanMee 作为线索,N^8 = N^4 * N^4 所以你不需要计算 N^4 两次。
  • 包装std::pow 的问题是它可以与double 一起使用,并且(至少在某些平台上)long long 的某些值不能用double 精确表示。
  • @KlitosKyriacou 好点。如果需要long long,我会编写一个专门化,明确转换为long double,以使用std::pow 的重载。
  • MS 仍然不支持超过 64 位双精度的任何内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-21
  • 1970-01-01
  • 2019-04-10
  • 2015-12-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多