浮点幂函数,无需递归

时间:2019-12-09 15:36:03

标签: math glsl pow exp

GLSL没有用于双精度类型的pow函数。此函数的经典实现使用递归。由于GLSL中不允许使用递归,因此我需要无递归的实现。

我还需要exp函数。

1 个答案:

答案 0 :(得分:4)

可以通过将其分为两个子问题来简单解决。

  1. 分离指数的全部和小数部分
  2. 计算整个部分的功效(这是微不足道的)
  3. 用小数部分计算功效(使用近似值)
  4. 乘以结果
  5. 如果初始指数为负,则反转结果
/// This is a simple non recursive implementation for pow, where the exponent is an integer.
double pow(double base, int power) {
    double res = 1.0;// Initialize result

    while (power > 0.0) {
        // If power is odd, multiply x with result
        if (power % 2 == 1) {
            res *= base;
        }

        // n must be even now
        power /= 2;
        base *= base;// Change x to x^2
    }

    return res;
}

// The desired precision. More precision results in slower execution.
const double EPSILON = 0.00000001;

double pow(double base, double power) {
    // We want to ignore negative exponents for now. We invert our result at the if necessary.
    bool negative = power < 0.0LF;
    if (negative) {
        power *= -1.0LF;
    }

    // Seperate the whole and fractional parts.
    double fraction = power - int(power);
    int integer = int(power - fraction);

    // The whole part is easily calculated.
    double intPow = pow(base, integer);

    // The fractional part uses an approximation method.
    double low = 0.0LF;
    double high = 1.0LF;

    double sqr = sqrt(base);
    double acc = sqr;
    double mid = high / 2.0LF;

    while (abs(mid - fraction) > EPSILON) {
        sqr = sqrt(sqr);

        if (mid <= fraction) {
            low = mid;
            acc *= sqr;
        } else {
            high = mid;
            acc *= (1.0LF / sqr);
        }

        mid = (low + high) / 2.0LF;
    }

    // Exponential rules allow us to simply multiply our results.
    double result = intPow * acc;

    // If we started with a negative exponent we invert the result.
    if (negative) {
        return 1.0LF / result;
    }

    return result;
}

const double E = 2.7182818284590452353602874LF;

/// e^x is as simple as that.
double exp(double power) { return pow(E, power); }

性能

exp(random(0.0, 100.0))的平均迭代次数:

EPSILON = 0.0001     -> 16.0763
EPSILON = 0.00001    -> 19.4108
EPSILON = 0.000001   -> 22.6639
EPSILON = 0.0000001  -> 26.0477
EPSILON = 0.00000001 -> 29.3929