自行实现log2对数运算
在应用中,需要实现以2为底对数运算,通过换底公式,可以将其转化为相对简单的运算,进而实现不同底数的对数运算。
一、数学原理
对数换底公式
对数变换级数
通过换底公式,ln2为常数,所以我们只需要求得ln(x)即可自行实现不同底数的对数运算。
所以我们通过上诉的对数变换级数去进行循环收敛获得一个相对准确的值。
通常适用于不包含数学库或者不支持浮点,要求硬件兼容性强的项目中
二、整形的ln(x)的C语言实现
整形此处因为对精度要求不高,只需要运算后能大致落在结果的附近即可满足设计需求。
下面对代码进行简单说明。
static unsigned int calc_api_lnd(unsigned int x)
{unsigned long long numerator;unsigned int denominator;unsigned long long y; /* (x-1)*1000/(x+1),整形转化 */unsigned long long y_sq; /* y*y *//* 循环运算 */unsigned long long sum;unsigned long long term;long long k; /* 迭代次数 */const unsigned int max_iter = 50; // 最大迭代次数,防止无限循环const unsigned int min_term = 100; // 终止阈值unsigned int result; /* 计算结果 */// 输入合法性检查if (x == 0){printf("Invalid input: x cannot be 0");return 0; // 无效输入返回0(可根据需求调整错误码)}if (x == 1){return 0; // ln(1) = 0,直接返回避免无效计算}// 计算缩放后的y值:y = ((x-1)/(x+1)) × 1000(整数近似)numerator = (unsigned long long)(x - 1) * 1000;denominator = x + 1;y = numerator / denominator;// 计算y²/1000(保持缩放比例),避免溢出if (y > 100000) // 过大的y直接计算平方会溢出,提前处理{y_sq = (y / 100) * (y / 100) * 10; // 分阶段计算避免溢出}else{y_sq = (y * y) / 1000;}sum = y;term = y;k = 1;printf("-->term=%lld, sum=%lld, k=%lld, y_sq=%lld", term, sum, k, y_sq);while (term > min_term && k < max_iter){// 检查下一次乘法是否会溢出if (term > ULLONG_MAX / y_sq){printf("Term would overflow, stopping early");break;}// 计算下一项:term = term * y_sq / 1000term *= y_sq;term /= 1000;sum += term / (2 * k + 1);k++;}// 防止最终结果溢出(如果sum过大,截断到合理范围)result = (2 * sum) > UINT_MAX ? UINT_MAX : (unsigned int)(2 * sum);printf("result=%u\r\n", result);return result;
}
三、浮点的ln(x)的C语言实现
因为浮点的不需要对小数部分做缩放处理,所以相对更加简单。代码如下
static double calc_api_lnf(double x)
{double y;double y_squared;double sum;double term;int k;const int max_iter = 100; // 最大迭代次数const double epsilon = 1e-12; // 精度阈值,控制迭代终止double result;// 输入合法性检查if (x <= 0.0){printf("Invalid input: x must be positive\n");return 0; // 无效输入返回NaN}if (x == 1.0){return 0.0; // ln(1) = 0,直接返回}// 计算核心变量 y = (x-1)/(x+1)y = (x - 1.0) / (x + 1.0);y_squared = y * y; // 预计算y的平方,避免重复计算sum = y; // 初始项:y^1 / 1term = y; // 当前项,初始为第一项k = 1; // 迭代计数器printf("-->term=%.10f, sum=%.10f, k=%d, y_sq=%.10f\n", term, sum, k, y_squared);// 手动判断term的绝对值是否大于epsilonwhile ((term > epsilon || term < -epsilon) && k < max_iter){// 计算下一项:term = term * y² (因为每一项是前一项乘以y²)term *= y_squared;// 分母为2k+1,累加至总和k++;sum += term / (2 * k - 1);}// 最终结果:ln(x) = 2 * sumresult = 2 * sum;// printf("result=%.10f\n", result);return result;
}