问题描述
我有兴趣写一个快速的C程序翻转双倍的指数。例如,这个程序应该将1e300转换为1e-300。我想最好的方法是一些操作,但是我缺乏足够的知识去完成这个任务。任何好主意?#include< math.h>
,因为它可能下溢的幅度非常大的值
double negate_decimal_exponent(const double value)
{
if(value!= 0.0){
const double p = pow(10.0,-floor(log10 (值))));
return(value * p)* p;
} else
返回值;
上面,
floor(log10(fabs(value) ))
是向下舍入的value
的绝对值的10的对数。本质上,它是使用科学记数法在value
中的十个指数的幂。如果我们否定它,并且把这个权力提高到10,那么我们就得到了10的幂的倒数。
我们不能计算 p
值
,或者对于非常小的值值溢出在数量上
。相反,我们用p
乘以value
,使得乘积的幅度接近于1(即十进制指数是零);然后再乘以p
,本质上否定十进制指数。
因为零的十进制的对数是未定义,所以我们需要分开处理。 (我最初错过了这个角落案例;感谢chux指出了它。)
这是一个示例程序来演示:
#include
#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 (值))));
return(value * p)* p;
} else
返回值;
#define TEST(val)printf(negate_decimal_exponent(%。16g)=%.16g\\\
,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;
$ / code>其中,编译时(记住要连接数学库
-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
方法来做到这一点?
当然。构建一个十的幂的查找表,并使用二分查找来找到幅值小于
值
的最大值。有第二个查询表有两个乘数,乘以值
,否定十的十进制幂。有两个因素是需要的,因为一个因素没有必要的范围和精度。 (但是,这两个值相对于以十为底的对数是对称的)。对于具有一千个指数的查找表(覆盖IEEE-754双打,但是应该在编译时检查它覆盖DBL_MAX
),这将是十个比较和两个乘法(使用浮点值),所以它会非常快。
一个可移植的程序也可以在运行时计算所需的表。
I am interested in writing a fast C program that flips the exponent of a double. For instance, this program should convert 1e300 to 1e-300. I guess the best way would be some bit operations, but I lack enough knowledge to fulfill that. Any good idea?
解决方案Assuming you mean to negate the decimal exponent, the power of ten exponent in scientific notation:
#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; }
Above,
floor(log10(fabs(value)))
is the base 10 logarithm of the absolute value ofvalue
, rounded down. Essentially, it is the power of ten exponent invalue
using the scientific notation. If we negate it, and raise ten to that power, we have the inverse of that power of ten.We can't calculate the square of
p
, because it might underflow for very large values ofvalue
in magnitude, or overflow for very small values ofvalue
in magnitude. Instead, we multiplyvalue
byp
, so that the product is near unity in magnitude (that is, decimal exponent is zero); then multiply that withp
, to essentially negate the decimal exponent.Because the base-ten logarithm of zero is undefined, so we need to deal with that separately. (I initially missed this corner case; thanks to chux for pointing it out.)
Here is an example program to demonstrate:
#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; }
which, when compiled (remember to link with the math library,
-lm
) and run, outputs (on my machine; should output the same on all machines using IEEE-754 Binary64 fordouble
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
Are there faster methods to do this?
Sure. Construct a look-up table of powers of ten, and use a binary search to find the largest value that is smaller than
value
in magnitude. Have a second look-up table have the two multipliers that when multiplied withvalue
, negates the decimal power of ten. Two factors are needed, because a single one does not have the necessary range and precision. (However, the two values are symmetric with respect to the base-ten logarithm.) For a look-up table with thousand exponents (covers IEEE-754 doubles, but one should check at compile time that it does coverDBL_MAX
), that would be ten comparisons and two multiplications (using floating-point values), so it'd be quite fast.A portable program could calculate the tables necessary at run-time, too.
这篇关于如何翻转双倍指数(例如1e300-> 1e-300)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!