GMP pow中的溢出处理

时间:2012-11-11 03:47:42

标签: c swi-prolog biginteger gmp yap

(我只是GMP库的间接用户,主要是通过。但我对解决这个问题非常感兴趣。)

当使用可笑的大值执行取幂时,主机系统或GMP不再能够适当地处理溢出。我已经与上述系统的开发人员进行了交谈,但他们没有看到一个简单的解决方案。

其他GMP系统/用户是否知道此问题?你如何处理这种溢出?

作为一个完整性检查首先测试7 ^ 7 ^ 7的值应该是:375982 ... 32343

在32位系统上,例如查询?- X is 13^1150000000.会产生这样的溢出。以下是YAP给出的内容:

GNU gdb (GDB) 7.0-ubuntu
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
...
Reading symbols from /opt/gupu/src/yap-6.3/narch-gupu2/yap...done.
(gdb) run -f
Starting program: /opt/gupu/src/yap-6.3/narch-gupu2/yap -f
YAP 6.3.2 (i686-linux): Sun Nov 11 04:19:37 CET 2012
?- X is 13^1150000000.

Program received signal SIGSEGV, Segmentation fault.
0x001638d8 in ?? () from /usr/lib/libgmp.so.3
(gdb) bt
#0  0x001638d8 in ?? () from /usr/lib/libgmp.so.3
#1  0x00164470 in __gmpn_mul_fft () from /usr/lib/libgmp.so.3
#2  0x001646c2 in __gmpn_mul_fft_full () from /usr/lib/libgmp.so.3
#3  0x00165f28 in __gmpn_sqr_n () from /usr/lib/libgmp.so.3
#4  0x0014b58b in __gmpz_n_pow_ui () from /usr/lib/libgmp.so.3
#5  0x0014c4a1 in __gmpz_pow_ui () from /usr/lib/libgmp.so.3
#6  0x080c4a1d in Yap_gmp_exp_int_int (i1=13, i2=1150000000) at ../C/gmp_support.c:939
#7  0x0815f9df in p_exp (t1=, t2=3082051592) at ../C/arith2.c:609
#8  0x080b1f19 in Eval (t=0) at ../C/eval.c:147
#9  0x080b2251 in p_is () at ../C/eval.c:186
#10 0x0806b56a in Yap_absmi (inp=0) at ../C/absmi.c:6912
#11 0x080b3655 in exec_absmi (top=) at ../C/exec.c:1002
#12 0x080b3b1f in do_goal (t=, CodeAdr=, arity=, 
    pt=0x0, top=1) at ../C/exec.c:1068
#13 0x080b3d1d in Yap_RunTopGoal (t=135918154) at ../C/exec.c:1291
#14 0x08061a6f in YAP_RunGoalOnce (t=135918154) at ../C/c_interface.c:2511
#15 0x0805c2f5 in do_top_goal (argc=2, argv=0xbffff4c4) at ../console/yap.c:84
#16 exec_top_level (argc=2, argv=0xbffff4c4) at ../console/yap.c:131
#17 main (argc=2, argv=0xbffff4c4) at ../console/yap.c:172
(gdb) 

编辑:对于64位系统也是如此;像这样:

Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 6.3.5)
Copyright (c) 1990-2012 University of Amsterdam, VU Amsterdam
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
Please visit http://www.swi-prolog.org for details.

For help, use ?- help(Topic). or ?- apropos(Word).

?- X is 3445^2^62.
gmp: overflow in mpz type
Abort

然而,

?- X is 2^2^63.
ERROR: Out of global stack
?- X is 2^2^62.
gmp: overflow in mpz type
Abort

从下面开始:

?- X is 2^2^36.
ERROR: Out of global stack
?- X is 2^2^37.
gmp: overflow in mpz type
Abort

因此,如果数字足够大,则SWI会检测到错误 - 因此可以由SWI处理(错误:消息来自SWI)。

5 个答案:

答案 0 :(得分:4)

不是真正的答案,而是对SWI-Prolog所做的解释。首先,它估计是否 可能会发生溢出。如果确定,则在调用GMP之前会引发错误。否则,它 依赖于GMP分配挂钩并在失败时执行longjmp()。它记录了哪些 为什么分配和释放为中止的GMP操作分配的内存。它 因为记忆永远不会受到GMP的控制,所以可以这样做。结果成功 GMP计算被复制到Prolog堆栈并受Prolog内存管理。

这曾经起作用,但在最近的版本中不起作用。我怀疑GMP估计的大小 如果它知道这将失败,甚至不打扰调用malloc()钩子。我所需要的只是一种方法,以确保始终调用钩子,即使有一个非常大的值。所有大于size_t的东西都可以用(size_t)-1来调用钩子。

P.S。由于复制到(较小的)Prolog运行时堆栈,它比内存可以更早地溢出。

答案 1 :(得分:3)

有些人为解决这个问题而做的事情(不支持并且泄漏了一些内存,但是他们发现它总比没有好):GMP允许你指定替换分配器(mp_set_memory_functions)。从这个分配器,你可以调用malloc,如果它失败你可以抛出一个C ++异常(如果你使用gcc,请用-fexceptions重新编译gmp)或调用longjmp或类似的东西来绕过GMP的失败处理并跳回你控制的代码。

答案 2 :(得分:2)

13 ^ 1,150,000,000约为2 ^ 4,255,505,675,代表4,255,505,675位。 每字节8位,大约500 MB的内存。 似乎它应该适合。

可能计算中涉及一些临时变量,它超出了流程大小限制。

答案 3 :(得分:1)

嗯,好像我运气不好:

Even the most recent version

   fprintf (stderr, "gmp: overflow in mpz type\n");
   abort ();

至少会处理此溢出,不能将其用作漏洞。

任何使用没有此问题的GMP的系统都必须使用修改后的库或复制用于估算大小的功能。

答案 4 :(得分:1)

看起来如果你有一个Cray,它会起作用。

#if defined (_CRAY) && ! defined (_CRAYMPP)
/* plain `int' is much faster (48 bits) */
#define __GMP_MP_SIZE_T_INT     1
typedef int         mp_size_t;
typedef int         mp_exp_t;
#else
#define __GMP_MP_SIZE_T_INT     0
typedef long int        mp_size_t;
typedef long int        mp_exp_t;
#endif