GMP整数的位数

19

有没有一种简单的方法来确定GMP整数的位数?我知道可以通过对数来确定它,但我想知道库中是否有什么东西我错过了。在手册中我找到的唯一一件事是:

_mp_size表示肢体的数量,或者当表示负整数时为其相反数。如果将_mp_size设置为零,则表示为零,此时_mp_d数据未使用。

但我认为这与我要找的东西相当不同。

124839 = 6个数字。


这是一个有趣的问题,给你点赞! - Nawaz
1个回答

18

您可以使用size_t mpz_sizeinbase (mpz_t op, int base)函数来获取在特定进制下表示该数所需的字符数。

size_t mpz_sizeinbase (mpz_t op, int base)

返回以给定进制为基数,op所需的位数。进制数范围从2到62。忽略op的符号,仅使用其绝对值。结果将是精确的或比实际多1个数字。如果基数是2的次幂,则结果始终是精确的。如果op为零,则返回值始终为1。

此函数可用于确定将op转换为字符串时所需的空间。分配的空间通常比由mpz_sizeinbase返回的值大两个单位,一个用于负号,另一个用于字符串结尾的null字符。

因此,类似以下内容:

size_t sz = mpz_sizeinbase (myNum, 10);

这应该是一个不错的开始。

如果你想要精确的大小,你可以使用那个值来创建一个足够大的缓冲区,将该值输出到该缓冲区,然后使用 strlen 获取更准确的大小,例如:

size_t sz = mpz_sizeinbase (myNum, 10) + 1; // allow for sign
char *buff = malloc (sz + 1);               // allow for `\0`
if (buff != NULL) {
    gmp_sprintf (buff, "%Zd", myNum);
    sz = strlen (buff);
    free (buff);
}

请注意,这并不是最高效的方法,因为每次查找长度时都会分配缓冲区,并且如果分配失败,则默认为最安全的大小,这可能比必要的大一个字节。

另一种可能的方法是使用更安全的snprintf选项,因为它返回被写入的字节数,并防止缓冲区溢出:

char oneChar;
int sz = gmp_snprintf (&oneChar, 1, "%Zd", myNum);

我没有专门测试过这个,但这是我以前用于“常规”C风格打印的技巧。

请注意,这两种“确切大小”的解决方案都包括可选符号在前面。如果您想真正计算数字而非字符的数量,则应进行调整(例如,如果数字小于零,则从大小中减去一)。


是的,它主要用于确定字符串需要多大,所以多一个也不太糟糕。从源代码来看,他们说你可以对前几个limbs(10000..或99999..)进行统计分析,但他们认为这对其设计目的来说是不必要的。也许你应该硬着头皮,根据mpz_sizeinbase分配空间,然后将值打印到其中并执行strlen。 - paxdiablo
3
请注意这句话:“结果要么是精确的,要么比实际值大1”。翻译为,您不能信任这个值。我曾因此函数固有的“差一错误”而受到损失...... - recursion.ninja
1
如果你被这个函数坑过,那不是因为这个“函数”本身有问题,而是因为你误解了(或者根本没去读)文档 :-) 它并没有固有的“差一”的错误,因为它明确说明了它会给你这样的结果(只有当函数的执行与描述不符时才算是 bug,其他情况都是实现选择),如果你按照预期使用这个值,是可以信任它的。如果你真的想要精确的大小,可以使用 sizeinbase()+2 来分配足够的空间,将值输出到该缓冲区中,并使用 strlen() 进行操作。 - paxdiablo
@Kundor,这个字节数的极限是包括终止符在内的,所以我认为代码本身是正确的。对于snprintf也是如此。你在两种情况下都出现了核心转储,这事实支持了这一点-几乎肯定是代码有问题。如果你想发帖提问并附上代码,你会得到比留下必要截断的评论更多的帮助。 - paxdiablo
@paxdiablo: 你说得对,我做了一些愚蠢的事情。(甚至可以说是令人尴尬的鲁莽:我将字符命名为“char c”,从而隐藏了外部作用域中的“mpz_t c”,然后执行了“gmp_snprintf(&c,1,“%Zd”,c)”...) - Nick Matteo
@Kundor,如果你每年不犯一个严重的愚蠢错误,那么你的职业生涯就已经停滞了。可以说,我的IT行业历史远非停滞不前 :-) - paxdiablo

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