我有兴趣编写一个快速的C程序来翻转double的指数。例如,该程序应将1e300转换为1e-300。我想最好的方法是一些操作,但我缺乏足够的知识来实现这一点。有什么好主意吗?
答案 0 :(得分:2)
假设你的意思是否定十进制指数,用科学记数法表示十指数的幂:
#include <math.h>
double negate_decimal_exponent(const double value)
{
if (value != 0.0) {
const double p = pow(10.0, -floor(log10(fabs(value))));
return (value * p) * p;
} else
return value;
}
在上面,floor(log10(fabs(value)))
是value
绝对值的基数10对数,向下舍入。基本上,它是value
中使用科学记数法的十指数的幂。如果我们否定它,并将十倍的力量提升到那个力量,我们就会得到十倍的力量。
我们无法计算p
的平方,因为它可能会在幅度非常大的value
值下溢,或者在value
的非常小的值上溢出。相反,我们将value
乘以p
,以使乘积的幅度接近一致(即十进制指数为零);然后将其乘以p
,基本上否定十进制指数。
因为零的十进制对数是未定义的,所以我们需要单独处理它。 (我最初错过了这个角落的情况;感谢chux指出它。)
以下是一个示例程序:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
double negate_decimal_exponent(const double value)
{
if (value != 0.0) {
const double p = pow(10.0, -floor(log10(fabs(value))));
return (value * p) * p;
} else
return value;
}
#define TEST(val) printf("negate_decimal_exponent(%.16g) = %.16g\n", val, negate_decimal_exponent(val))
int main(void)
{
TEST(1.0e300);
TEST(1.1e300);
TEST(-1.0e300);
TEST(-0.8e150);
TEST(0.35e-25);
TEST(9.83e-200);
TEST(23.4728395e-220);
TEST(0.0);
TEST(-0.0);
return EXIT_SUCCESS;
}
当编译时(记得与数学库链接,-lm
)并运行,输出(在我的机器上;应该在使用IEEE-754 Binary64的double
s的所有机器上输出相同的内容):
negate_decimal_exponent(1e+300) = 1e-300
negate_decimal_exponent(1.1e+300) = 1.1e-300
negate_decimal_exponent(-1e+300) = -1e-300
negate_decimal_exponent(-8e+149) = -8e-149
negate_decimal_exponent(3.5e-26) = 3.5e+26
negate_decimal_exponent(9.83e-200) = 9.83e+200
negate_decimal_exponent(2.34728395e-219) = 2.34728395e+219
negate_decimal_exponent(0) = 0
negate_decimal_exponent(-0) = -0
是否有更快的方法来执行此操作?
不确定。构造一个10的幂查找表,并使用二进制搜索来查找幅度小于value
的最大值。让第二个查找表具有两个乘法器,当乘以value
时,取消十进制的十进制幂。需要两个因素,因为单个因素没有必要的范围和精度。 (但是,这两个值相对于十对数对称是对称的。)对于具有千个指数的查找表(涵盖IEEE-754双精度数,但是应该在编译时检查它是否涵盖DBL_MAX
),这将是十次比较和两次乘法(使用浮点值),因此它非常快。
便携式程序也可以在运行时计算必要的表格。