C#中值类型BigInteger的限制是多少?

时间:2014-02-18 14:27:18

标签: c# .net numerical value-type

如MSDN BigInteger中所述:

  

表示任意大整数的不可变类型   理论上的价值没有上限或下限。

我可以看到BigInteger是ValueType,据我所知,ValueType的最大大小必须为16字节

MSDN进一步说:

  

对于导致a的任何操作,都可以抛出OutOfMemoryException   BigInteger值增长过大。

以及更多:

  

虽然这个过程对调用者来说是透明的,但确实会产生一个问题   性能惩罚。在某些情况下,尤其是重复时   操作在非常大的BigInteger值上循环执行

它如何存储像double.MaxValue + double.MaxValue这样大的值? 我被告知它内部有ReferenceType个对象,但我在VisualStudio中定义的所有内容都是ValueTypes。

它的真正限制是什么?即使没有,它如何“作为一种价值类型”设法存储所有数据量?

4 个答案:

答案 0 :(得分:17)

  

正如我所知,BigInteger是一个ValueType,据我所知,ValueType的最大大小必须为16个字节。

不,那不是真的。这是传统的限制,但是对于值类型而言,完全可行。例如:

public struct Foo {
    private readonly int a, b, c, d, e; // Look ma, 20 bytes!
}

但是,我强烈怀疑BigInteger实际上包含对字节数组的引用:

public struct BigInteger {
    private readonly byte[] data;
    // Some other fields...
}

Moslem Ben Dhaou's answer使用intuint[]显示了一个当前实现,但当然有意隐藏详细信息。)

所以BigInteger仍然很小,但它可以引用一大块内存 - 如果没有足够的内存来分配你需要的内容执行一些操作,你会得到一个例外。

  

它怎么能存储像double这样的大值.MaxValue + double.MaxValue?

BigInteger适用于整数,所以我不会特别想将它用于与double有关的任何事情......但从根本上说,限制是你可以获得多少内存以及CLR可以应对的数组大小。实际上,在实际达到任何特定数字的限制之前,你会谈论巨大的数字 - 但是如果你有数量较少的数字,那显然也有很大的内存需求。

答案 1 :(得分:5)

作为对Jon Skeet答案的确认,我查看了BigInteger的源代码。它实际上包含两个内部属性,如下所示:

internal int _sign;
internal uint[] _bits;
几乎所有用于读取/写入实际数据的私有/公共方法都使用

_bits

_sign用于保留BigInteger

的符号

私有方法广泛使用二元运算符和计算。以下是类中使用的一小部分常量,可能反映了一些限制:

private const int knMaskHighBit = -2147483648;
private const uint kuMaskHighBit = 2147483648U;
private const int kcbitUint = 32;
private const int kcbitUlong = 64;
private const int DecimalScaleFactorMask = 16711680;
private const int DecimalSignMask = -2147483648;
PS:我应该对J.S.发表评论。回答,但评论太短。要查看源代码,请download it或反编译System.Numerics.dll

答案 2 :(得分:0)

TL; DR:BigInteger的最大值为2 ^ 68719474912

在.Net 4.7.2中,BigInteger使用uint数组存储位。

一个uint可以容纳32位数据。

数组的最大大小定义为internal const int MaxByteArrayLength = 0x7FFFFFC7; 7FFFFFC7 = 2147483591

现在,要计算:数组的最大大小x每个uint的容量是:2147483591 x 32 =68719474912。但这只是BigInteger中的位数。

这意味着BigInteger的最大值是:2 ^ 68'719'474'912,这是惊人的大(为了便于阅读,使用了'。)

如果他们决定增加数组的最大大小,那么也会增加BigInteger的最大值。

答案 3 :(得分:0)

我刚刚对此做了一些快速实验。 Max 似乎在 2^65,000,000,000 左右,但实际实用性 2146435071

我在下面的 0x1F 处收到 System.OverflowException。它在 E FFFF FFE2 和 F 7FFF FFE1 之间溢出。 (或介于 2^64,424,509,410 和 2^66,571,993,057 之间)这实际上非常接近 Csaba Benák 的答案。

// Test 1
BigInteger test = 1;
for (int i = 0x00; i < 0xFF; i++)
    test <<= 0x7FFFFFFF;

// Test 2
BigInteger.Pow((BigInteger)2, 0x7FEFFFF0); // OK - I think - never finished
BigInteger.Pow((BigInteger)2, 0x7FEFFFFF); // Immediate OutOfMemoryException

我还应该注意到,虽然似乎支持 ~66,571,993,057。有用性更像是 2^2146435071,因为 POWER 和班次似乎不适用于大于 2,146,435,071(对于 POW() )或班次量超过 2,147,483,647 的 POWER。可以进行更大的轮班,但需要几轮才能破坏效率。另一个项目在这些速度下很慢 - 一个班次大约需要 7 秒,而 BigInteger.Pow() 至少需要 5 分钟。

.Net 5、AMD Threadripper、32GB 内存、Windows 10 x64