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

wee*_*mer 4 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)); 
Run Code Online (Sandbox Code Playgroud)

438*_*427 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);
Run Code Online (Sandbox Code Playgroud)

结果是:

5.01237e-3011
5.01237e-3011
0.5
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

从这里开始: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)); 
Run Code Online (Sandbox Code Playgroud)

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


Sim*_*rne 5

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

  • 可以使用log-sum-exp技巧来执行加法/减法(和更一般地求和),即

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

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

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

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


Gab*_*han -2

编译器不知道足够的数学知识来简化这样的复杂代码(编译器不知道 pow 实际做了什么,它只知道它是一个接受一种类型并返回另一种类型的函数)。所以实际上是要一步步进行计算的。不幸的是,结果太小,无法放入双精度数中。这就是它返回 NAN 的原因 - 您触发了变量的下溢。

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

或者您可以迁移到 Mathematica 或 MatLab 这样的平台,它们更适合此类工作,并且内置了很多此类问题。

  • 我认为不涉及任何下溢。这是一个简单的“舍入到零”或“舍入到最接近的”,导致中间结果为零。然后是 0/(0+0),即 NaN。 (2认同)