如何翻转双精度的指数(例如1e300-> 1e-300)?

时间:2017-07-04 00:24:52

标签: c floating-point

我有兴趣编写一个快速的C程序来翻转double的指数。例如,该程序应将1e300转换为1e-300。我想最好的方法是一些操作,但我缺乏足够的知识来实现​​这一点。有什么好主意吗?

1 个答案:

答案 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 ),这将是十次比较和两次乘法(使用浮点值),因此它非常快。

便携式程序也可以在运行时计算必要的表格。