如MSDN BigInteger中所述:
表示任意大整数的不可变类型 理论上的价值没有上限或下限。
我可以看到BigInteger是ValueType
,据我所知,ValueType的最大大小必须为16字节。
MSDN进一步说:
对于导致a的任何操作,都可以抛出OutOfMemoryException BigInteger值增长过大。
以及更多:
虽然这个过程对调用者来说是透明的,但确实会产生一个问题 性能惩罚。在某些情况下,尤其是重复时 操作在非常大的BigInteger值上循环执行
它如何存储像double.MaxValue + double.MaxValue
这样大的值?
我被告知它内部有ReferenceType
个对象,但我在VisualStudio中定义的所有内容都是ValueTypes。
它的真正限制是什么?即使没有,它如何“作为一种价值类型”设法存储所有数据量?
答案 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使用int
和uint[]
显示了一个当前实现,但当然有意隐藏详细信息。)
所以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