没有那么多 math.h 函数,有什么更简单的方法来制作对数函数?

Without that much of math.h functions, what's a simpler way of making a logarithm function?

我没有完全帮到我,虽然它给了我一些细节。

我当前的基础代码:

#define e 2.7182818284590452353602874713527

double logarithm(long double dec) {
    // return log10f(dec);
    return result;
}

我从链接的问题中学到了什么:

  1. 对数函数使用e as base.
  2. 此外,根据公认的答案,^ 不适用于指数(我正计划将此语法设为指数形式,这可能意味着放弃 power() 函数),而是异或运算符。
  3. 有人回答了我的问题,但被删除了。它是关于 log10f 源代码的,但它有点令人困惑,所以对我来说不值得。

现在我对这个函数的标准是它是从头开始的,没有多重函数,并且是一个简单的排序(尽管我怀疑对数甚至不简单)。

答案代码:

#include <math.h>

float log_num(int num) {
    return log10f(num);
}

是的,相当简单,但作为一个挑战,我不想自己使用 log() 函数,尽管我仍然依赖 <math.h> 的函数。

问题:在没有内置对数函数的情况下,从头开始制作对数函数的简单形式是什么? 如果不使用<math.h>,那怎么样?

虽然肯定不是最好的,但我认为一种非常简单的方法是创建一个函数,该函数对所有可能的答案执行二进制搜索,直到找到正确的答案。

伪代码

#define e 2.7182818284590452353602874713527

double logarithm(long double dec) {
  double answer = <<half of max>>;
  double step = <<quarter of max>>;
  while(!done) {
    if (exponent(e,answer) == dec) {return answer};
    else if (exponent(e,answer) > dec) { answer -= step;}
    else if (exponent(e,answer) < dec) { answer += step;}
    step /= 2;
  }
}

我想强调一下,这效率不高,未经测试,可能会使您的计算机自毁。

第一句话:所有对数都是成比例的。这意味着如果您有一个计算二进制对数的函数,您可以使用它来推导出十进制对数或自然(底数 e)对数。特别是 log10(x) = log2(x) / log2(10).

第二个备注:对于一个数nceil(log10(n+1))n的位数,当n是用十进制表示。同理,ceil(log2(n+1))n写成二进制时n的位数。计算机中的数字以二进制表示,因此获取位数应该不会太难。这为我们提供了整数 n >= 1.

的近似值 log2(n) ≃ nbbits(n) - 1

第三条评论:计算位数适用于整数。我们可以通过将其四舍五入为 int 来扩展到大于 1 的 double。对于 0 和 1 之间的 double x,我们可以使用公式 log(x) = -log(1/x) 并将我们的近似值应用于 1/x.

#define log_10_of_2 (0.30102999566)
#define log_e_of_2 (0.69314718056)

unsigned int nbbits(unsigned int n)
{
    unsigned int count_bits = 0;
    while (n != 0)
    {
        n = n >> 1;
        ++count_bits;
    }
    return count_bits;
}

double approx_decimal_log(double x)    /* assume x > 0 */
{
    if (x < 1)
        return -approx_decimal_log(1/x);
    return (nbbits((unsigned int) x) - 1.0) * log_10_of_2;
}

此近似值的精度约为 0.3。

这是因为nbbits在二进制对数1以内,我们乘以0.3得到十进制对数

如果您需要相当准确的信息,可以使用 Taylor series 公式。如果您需要更高的精度,请增加迭代次数。

#define EULER_CONST 2.718281828459045235
#define TAYLOR_ITERATIONS 20

double nat_log(double x) {
    // Trap illegal values
    if (x <= 0) {
        return 0.0/0.0;  // NaN
    }
    
    // Confine x to a sensible range
    int power_adjust = 0;
    while (x > 1.0) {
        x /= EULER_CONST;
        power_adjust++;
    }
    while (x < .25) {
        x *= EULER_CONST;
        power_adjust--;
    }
    
    // Now use the Taylor series to calculate the logarithm
    x -= 1.0;
    double t = 0.0, s = 1.0, z = x;
    for (int k=1; k<=TAYLOR_ITERATIONS; k++) {
        t += z * s / k;
        z *= x;
        s = -s;
    }
    
    // Combine the result with the power_adjust value and return
    return t + power_adjust;
}