Search code examples
c#bigintegerarithmeticexception

Why am I getting a System.ArithmeticException with C#'s Nuget BigInteger 1.0.7?


So, I've been learning C# and testing some simple algorithms out. I made this simple class that exposes a recursive Fibonacci number function. I use memoization (Dynamic Programming) to store previously found numbers. Here's the code:

    using Godot;
    using System.Collections.Generic;

    public class Exercise1 : Node {
      private BigInteger teste = new BigInteger(1);
      private Dictionary<BigInteger,BigInteger> memory = new Dictionary<BigInteger,BigInteger>();
      public override void _Ready() {
        RunBigIntegerCraziness();
      }

    private void RunBigIntegerCraziness() {
      for (int i = 0; i < 31227; i++) {
        GD.Print($"fib number {i} is {fib(new BigInteger(i))}");
      }
    }

    private BigInteger fib(BigInteger n) {
      if (memory.ContainsKey(n)) {
        return memory[n];
      }

      if (n <= 2) {
        memory[n] = 1;
        return 1;
      }

      memory[n - 2] = fib(n - 2);
      memory[n - 1] = fib(n - 1);

      return memory[n - 2] + memory[n - 1];
    }
    }

Ignore the "Godot" part. It's just that I was testing this inside a game project. Everything compiles fine, but I can only calculate up to Fibonacci n. 3226. If I go to numbers equal to 3227 and beyond, I get this Exception:

[...] fib number 3225 is 43217018697618272220345809139733426666656338842625944764401661804465121290773093888861438958973337206110398501101783011185091567135587979099219045977958276652741787987152919489957724618258731270111934419344108965974546742136386635343927537356176338553654798753734888560554135669621772542530920892471422002609630627040146600381068673360870794221630560104764217344676242315795514744073614579107596818134891238017641931792490286597416223216551326908997909707498658766245465906764466010328772845314077258564566442129155001040721886128505146365749238671331993692655687520038382893117763783477305776640877748401894737521738794911907045829607125767696264441933278046913082328818850 fib number 3226 is 69926605145186078778460989556214883682824163521963475389625827936774132010815037784261200216187927277027753726906285824183824754568491676416709800266452379691484582909141867315765704538889919267496081895355700988068705924800720430980434359659981529442293650167261872365958477365094269319478110803029308487644284790516508517647989046631899202985143468253781566270183590285229230335042129126551683888682955813507183937267823895233985645240278207971782178625906849647650415867576295127507836850507509010403410481726883571748090361307264480634218098397060429202475118649538779225621232854604363989464362465170636407301900981359138471646464444082736135056091569488488491377766743

Unhandled Exception: System.ArithmeticException: Overflow or underflow in the arithmetic operation. at BigInteger.op_Addition (BigInteger bi1, BigInteger bi2) [0x000fa] in :0 at Exercise1.fib (BigInteger n) [0x000a8] in /Users/rafael/gamedev/godot/mytests/CSharpStudy/study_classes/Exercise1.cs:31 at Exercise1.RunBigIntegerCraziness () [0x00006] in /Users/rafael/gamedev/godot/mytests/CSharpStudy/study_classes/Exercise1.cs:15 at Exercise1._Ready () [0x00001] in /Users/rafael/gamedev/godot/mytests/CSharpStudy/study_classes/Exercise1.cs:10 The terminal process terminated with exit code: 1

Aren't "BigInteger"s supposed to handle pretty high numbers??


Solution

  • In the source of that BigInteger, there is:

    // maximum length of the BigInteger in uint (4 bytes)
    // change this to suit the required level of precision.
    private const int maxLength = 70;
    

    The length is counted in limbs, each limb is 32 bits in this implementation. Additionally, the topmost bit of the topmost limb is treated as a sign bit. Therefore, without changing the source the maximum number that can be stored in this kind of BigInteger (this limit does not apply to the BigInteger in System.Numerics) is 270*32-1-1, or in other words, there are 2239 "normal" bits available.

    That allows for some fairly big integers, but not big enough: according to Wolfram Alpha fib(3227) requires just over 2239 bits, therefore it does not fit.