开发者

Is there a good, performant unsigned BigInteger type for C#?

开发者 https://www.devze.com 2023-03-05 04:48 出处:网络
I\'m familiar with the System.Numerics.BigIntege开发者_开发知识库r class, but in my app, I\'m only ever dealing with positive integers. Negative integers are an error case, and it\'d be nice if there

I'm familiar with the System.Numerics.BigIntege开发者_开发知识库r class, but in my app, I'm only ever dealing with positive integers. Negative integers are an error case, and it'd be nice if there was an unsigned equivalent of the BigInteger type so I could remove all of these checks. Does one exist?


There's nothing in the framework, no. I would try to centralize the checks in as small a public API as possible, and then treat the data as valid for the rest of the time - just as you would for something like null checking. Of course, you still need to be careful if you perform any operations which could create a negative value (e.g. subtracting one from another).

You may be able to make the code slightly neater by creating an extension method, e.g.

public static void ThrowIfNegative(this BigInteger value, string name)
{
    if (value.Sign < 0)
    {
        throw new ArgumentOutOfRangeException(name);
    }
}

... and use it like this:

input.ThrowIfNegative("input");

You could potentially create your own UBigInteger struct which contained a BigInteger, and perform operations between different values by using the BigInteger implementation and checks, but I suspect that would be quite a lot of work for relatively little benefit, and may have performance implications if you're using it for a lot of calculations.


Well, let's have a look at a simple example

        uint a=1;
        uint b=2;
        uint c=a-b;
        Console.WriteLine(c);

gives you the output 4294967295 (=2^32-1).

But what if you had an unsigned BigInteger with similar behaviour?

        UBigInteger a(1);
        UBigInteger b(2);
        UBigInteger c=a-b;
        Console.WriteLine(c.ToString());

What should that be? Of course, from what you wrote one can assume you might expect to get some kind of exception in this case, but such behaviour would not be consistent to int. Better introduce the checks for < 0 where you need them, for example, like the way Jon Skeet suggested.


If you only use a reasonably small subset of the the BigInteger API, writing your own wrapper class is easy if laborious. Here is some sample code to demonstrate that it needn't be that big an operation:

public struct UnsignedBigInteger
{
    private BigInteger value;
    private UnsignedBigInteger(BigInteger n) { value = n; }

    public UnsignedBigInteger(uint n) { value = new BigInteger(n); }
    // ... other constructors ...

    public static UnsignedBigInteger operator+(UnsignedBigInteger lhs, UnsignedBigInteger rhs)
    {
        return new UnsignedBigInteger(lhs.value + rhs.value);
    }
    public static UnsignedBigInteger operator-(UnsignedBigInteger lhs, UnsignedBigInteger rhs)
    {
        var result = lhs.value - rhs.value;
        if (result < BigInteger.Zero) throw new InvalidOperationException("value out of range");
        return new UnsignedBigInteger(result);
    }
    // ... other operators ...
}


If negative values present such a problem, is possible to eliminate them somehow so that bad data(the negative values) doesn't even reach your logic ? This will eliminate the check all together. Could you post a short snippet of what you are doing ?


There isn't any support in the framework to declare BigInteger as unsigned. However, you could create a static method to check if the number is negative or not.

public static void ValidateBigIntForUnsigned(BigInteger bigInteger)
{
   if(bigInteger.Sign < 0)
       throw new Exception("Only unsigned numbers are allowed!");
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号