在C ++中处理极小的数字

时间:2015-07-08 20:47:16

标签: c++ floating-point floating-accuracy

设a和b为0和1之间的两个数字。 如何计算pow(a,10000)/(pow(a,10000)+pow(b,10000))

例如: - 以下代码给出-nan作为输出而不是0.5

double a = 0.5,b = 0.5; 
cout<<pow(a,10000)/(pow(a,10000)+pow(b,10000)); 

3 个答案:

答案 0 :(得分:6)

您的问题没有简单的通用解决方案。编写处理非常小和/或非常大数字的计算机程序是一种“科学艺术” - 通常称为数值分析。典型的技巧包括计算前的缩放。

在您的情况下,每个pow(..)都舍入为零,因为这是与实际结果最接近的可表示值。之后你做0 /(0 + 0)即NaN,即不是数字。

你可以长篇大论:

long double a = 0.5;
long double b = 0.5;
long double c =pow(a,10000);
long double d =pow(b,10000);
cout << c << endl;
cout << d << endl;
cout<<c/(c+d);

导致:

5.01237e-3011
5.01237e-3011
0.5

但这只会有助于“一段时间”。稍微增加功率(只是额外的零)并且问题又回来了。

long double a = 0.5;
long double b = 0.5;
long double c =pow(a,100000);
long double d =pow(b,100000);
cout << c << endl;
cout << d << endl;
cout<<c/(c+d);

0
0
nan

因此,您需要自己编写一个非常复杂的类或研究如何在数值分析中处理它。

从这里开始:https://en.wikipedia.org/wiki/Numerical_analysishttps://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic

如果您知道所有三个地方的exp都相同,那么您可以这样做:

double a = 0.5,b = 0.5; 
int exp = 10000;
//cout<<pow(a,exp)/(pow(a,exp)+pow(b,exp)); is the same as:
cout<<1/(1+pow(b/a,exp)); 

对于大多数a和b值,它会更好,但不要指望任何精度。如果a和b略有不同,你会得到0(少于b)或1(b少于a)。但NaN部分将得到解决。

答案 1 :(得分:5)

此类问题的标准方法是使用日志空间,即将每个数字表示为e x ,其中x是您的标准浮点类型:

  • 可以使用log-sum-exp trick进行加/减(以及更一般的求和),即

    • e x + e y = e x (1 + e yx )= e x + log(1 + exp(yx))
  • 乘法/除法成为加法/减法

    • e x ×e x = e x + y
  • 提升到一个幂是乘以指数:

    • pow(e x ,e x )= e x exp(y)

但在您的特定情况下,您可能最好使用StillLearning答案结尾处建议的方法

答案 2 :(得分:-1)

编译器不知道足够的数学能够像这样简化复杂的代码(编译器不知道pow实际上做了什么,它只知道它的一个函数,它接受1种类型并返回另一种类型)。所以它实际上必须逐步完成计算。不幸的是,结果是如此之小,以至于它不适合双人。这就是它返回NAN的原因 - 你触发了变量的下溢。

如果你真的需要这样做(我问你为什么需要那么高的准确度),你需要做一些数学技巧来使用非标准数字系统(对于例如,不是存储美元,而是存储便士 - 但使用更高的因子),或者您可以编写自己的数学类并完成所有自己的数学库。

或者你可以转到像Mathematica或MatLab这样的平台,这种平台对于这类工作更好,并且内置了很多这类问题。