【问题标题】:What is the limit of the Value Type BigInteger in C#?C# 中值类型 BigInteger 的限制是什么?
【发布时间】:2014-02-18 14:27:18
【问题描述】:

如 MSDN 中所述BigInteger 是:

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

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

MSDN 进一步说:

对于任何导致 BigInteger 值增长过大。

还有更多:

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

它如何存储像double.MaxValue + double.MaxValue 这样大的值? 有人告诉我,它里面有 ReferenceType 对象,但我在 VisualStudio 的定义中只能找到 ValueTypes。

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

【问题讨论】:

  • 那 16 个字节是一个(非常)软建议。您可以拥有数千字节的结构,以及具有内部数组(引用类型)的结构。

标签: c# .net numerical value-type


【解决方案1】:

据我所知,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 可以处理的数组大小。实际上,在实际达到任何特定数字的限制之前,您会谈论 巨大的 数字 - 但如果您有大量较小的数字,那显然也需要大量内存。

【讨论】:

  • “Moslem Ben Dhaou 的回答显示了一个使用 intuint[] 的当前实现,但当然,其中的细节是故意隐藏的” i> The Reference Source begs to differ ;)
  • @ColeJohnson:嗯,它们是“隐藏的”,因为它们不是公共 API 的一部分——它们可以随时更改。
【解决方案2】:

为了确认 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

【解决方案3】:

TL;DR:BigInteger 最大值为 2^68685922272

在 .Net 4.7.2 BigInteger 中使用 uint 数组表示位。

一个 uint 保存 32 位数据。

数组的最大尺寸定义为internal const int MaxArrayLength = 0X7FEFFFFF; 7FEFFFFF = 2146435071

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

这意味着 BigInteger 的最大值是:2^68'685'922'272 非常大(使用 ' 以便于阅读)。

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

【讨论】:

    【解决方案4】:

    我刚刚对此做了一些快速实验。 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 RAM、Windows 10 x64

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-18
      • 1970-01-01
      • 2021-10-24
      • 1970-01-01
      • 2016-03-10
      • 2011-05-23
      • 2010-10-24
      • 1970-01-01
      相关资源
      最近更新 更多