使用GMP计算公式中的超大整数

3
我很难解决遇到的问题。作为一个复杂公式的一部分,我需要计算一个很容易超出double范围的部分,也就是说结果达到了~1.59*10^(1331)(使用Mathematica计算)。当然这已经超出了double的范围。然后我想使用long double,在我的Linux系统上,它是16个字节(gcc 4.6.3)。
1)双精度(8个字节)的可能范围高达10^(308)。我说得对吗?长双精度是否增加了实际精度,但没有增加可能的数字值范围?我记得我听说过,这取决于系统和编译器。是真的吗?至少当我尝试用long double计算我的值时,仍然会得到NaN。
2)然后我寻找一种方法来实际计算这些结果,我发现GNU的gmp。我听说你可以表示非常大的整数,我想这可能有所帮助。然而,阅读文档后,似乎...
 mpz_t x;
 mpz_init(x);
 mpz_set_*(x,#);

我可以为gmp整数数据类型分配值,但为了做到这一点,我只能选择分配可以用内置数据类型(如double或(u/s)int等)表示的值。 我发现有关如何分配非常大的数字的所有内容都是使用mpz_set_str()从字符串中分配数字。

如果我想要分配一个复杂计算的结果呢?简单地说,公式看起来像这样:

long double res1,res2=0.0;
int a,b;
a=780;
b=741;
float d,d1,o,s; // can be values in [0.01,100]

res1=(2*(pow(b,2)*pow(E,b*(o + s))*(pow(d1,2) + pow(E,a*s)*(-1 + pow(E,a*o)) + pow(d,2)*(-1 + pow(E,a*s))) + pow(a,2)*pow(E,a*(o + s))*(pow(E,b*s)*(pow(E,b*o) + (-1 + d)*(1 + d + b*o)) + (-d + d1)*(d + d1 + b*o + b*d*s)) - a*b*(pow(d1,2)*(pow(E,a*(o + s)) + pow(E,b*(o + s))) + pow(E,a*o + (a + b)*s)*(-2 + 2*pow(E,b*o) - b*o) + d1*pow(E,a*s)*(-pow(E,b*o) + pow(E,a*o)*(1 + b*o)) + pow(d,2)*pow(E,a*s)*(-pow(E,b*o) + pow(E,b*(o + s)) + pow(E,a*o)*(-1 + pow(E,b*s) - b*s)) +  d*(-(d1*pow(E,b*(o + s))) + (1 + d1)*pow(E,b*o + a*s) - pow(E,a*s + b*(o + s)) + pow(E,b*s + a*(o + s))*(1 + b*o) + pow(E,a*(o + s))*(-1 - b*o + b*d1*s)))))......;

res2也将是这种类型,最后我需要计算res1/res2,通常会得到一个非常小的数字。

我考虑将公式拆分并向mpg_z添加项,以便不会超出每个项的双精度范围,但由于公式如此漫长和复杂,这几乎是不可能的。

因此,问题在于我的中间结果可能会变得非常巨大,没有任何数据类型能够存储它们,因此我无法将其分配给mpz并摆脱这个问题。

我知道我想要计算double值,并实际上使用mpz_t来处理整数。据我所了解,这是存储这样大的数据的唯一方法,因为mpf_t只能处理浮点类型。老实说,关于中的表示仍然让我感到困惑。

有什么想法如何解决这个问题吗?


考虑使用所有的mpz_t变量。放弃整数变量(在我能看到的代码部分中被提升为双精度),你不会因为使用它们而获得任何好处。从函数的一开始就将所有变量都设为mpz_t类型。顺便问一下,你想做什么? - jim mcnamara
1
看起来你没有进行整数算术运算,所以我建议你坚持使用 mpf_t 并使用 gmp 提供的浮点算术实现你的整个公式。 - mwv
这是理论种群遗传学,我正在尝试计算给定任意人口分段函数的互同祖时间矩。 - theuni
1
以上的评论仍然正确。如果您想在算法的某些部分使用GMP,那么最好的方法是将整个算法都使用GMP。在我个人的经验中,GMP实际上并不比本地整数慢太多。此外,如果您真正在进行浮点数数学运算,我可以推荐使用MPFR(http://www.mpfr.org/)而不是GMP。 - CmdrMoozy
您可能希望使用C++接口,以避免为公式的各个部分创建任意临时变量。 - Mihai Todor
1个回答

2

问题1 long double可以处理比double更大的数字(指数和有效位精度)。但是,如果您的目标是存储大整数,那么您必须考虑到您的1e308数量级并不意味着任何东西;您只需要关心有效位精度的大小,无论是52/53位(double)还是64位(x86扩展精度)。如果您尝试将其用于更大的整数,则会有一个正确的数量级,但确切的值将丢失(在使用整数时,人们通常会对此更加关注,而在使用近似数字时则不太关注)。

问题2 使用GMP是一个不错的选择。其他库也存在;对于较小的值,我经常使用具有扩展固定精度并且非常快速的libqd,但这对于您自己的问题来说是不够的。现在您的问题是如何设置值:

  • 使用数字的字符串版本通常是一个坏主意(您应该仅将其保留用于输入/输出目的);它是一种涉及基础转换和逐位处理的缓慢操作
  • 尽可能多地使用GMP类型是正确的方法(除非您真的关心速度并且完全控制要使用本机类型计算的值的预期范围
  • 如果您的公式太长,我无法提供太多帮助。但是使用GMP并不那么困难,您真的不能转换您的公式吗?您的公式中是否有一些逻辑可以嵌入循环中?也许您可以编写一个快速而肮脏的Python脚本,将您的公式转换为使用GMP的C代码片段?

现在我并不完全理解为什么您想使用mpz_t而不是mpf_t。这种类型实现了任意长的浮点数;您是否注意到可以使用mpf_set_default_prec设置精度?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接