1

In C#, I need to work with very large (and very small) numbers, for which decimal and double are not accurate enough and BigInteger is not able to store a number's fractions.

I'd like to have the numbers to have as long components i.e. the characteristic and the mantissa, as memory (and preferably hard drive) space will allow.

Does someone have a class or is there a system type for a really big number.

I need to be able to add, subtract, divide, modulus, square, square-root, sin, cos, tan (and their inversions) and multiply the number. Pretty much the complete functionality of a standard Decimal/Double (in case I've missed any).

Infinity needn't be represented, but it would be a plus*!

An example of a very small number is:

0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001

and examples of very large numbers are:

1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001

and

-1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001

I'd prefer ToString() to return the number in the form described above. Scientific notation is acceptable, but by no means preferred.

Four most important requirements are:

  1. the accuracy of the number
  2. At least, basic maths operations can be applied; multiply, division, addition and subtraction
  3. The number must only be limited by the size of spare memory and harddrive.
  4. Number must output as a string equivalent in the form Characteristic, decimal point and then mantissa e.g. 100.23, 100 or 0.000000054
  5. There should be support for reccurance in the mantissa

BigInteger is not an acceptable answer.

*If infinity is implimented, then I need only implement it as far as possible e.g. (infinity / infinity = 1), (0 / infinity = 0), etc.

WonderWorker
  • 8,539
  • 4
  • 63
  • 74
  • 2
    Look for [`BigInteger`](https://msdn.microsoft.com/en-us/library/system.numerics.biginteger(v=vs.110).aspx). – Yuval Itzchakov Jul 29 '15 at 13:24
  • 1
    http://stackoverflow.com/questions/10293603/arbitrarily-large-integers-in-c-sharp – Mohammad Faizan khan Jul 29 '15 at 13:26
  • BigInteger is only suitable for integers. A solution that makes use of BigInteger to store the the characteristic and the mantissa within a class, or possibly a floating point implimentation would be perfect, but BigInteger by itself is not a solution. – WonderWorker Jul 31 '15 at 11:44
  • I typed the numbers the way I did to make it as easy to understand as possible: Even a pre-schooler stands a chance at understanding the question this way. I only want scientific notation to be used if a flag is set, or maybe a method called ToScientificNotationString() or similar. By default I'd prefer the above. – WonderWorker Jul 31 '15 at 13:36
  • 4
    I'd say that your requirement #3 reduces the probability of finding a solution to around 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001. – OldFart Aug 10 '15 at 21:26
  • 1
    @OldFart Good sense of humour lol – WonderWorker Aug 11 '15 at 07:35
  • Maybe you can try to find another programming language that supports calculation’s with very small and large numbers, or have library’s that support it. – Gaotter Aug 17 '15 at 15:04
  • It's not a trivial task to make such a type. For example you wanted to use whole accessible memory. If you will use 'value' representation of a number for that type (ex. base 10 like human readable) when you will perform divide operation (ex. 1/3) result may take whole memory. – Logman Aug 17 '15 at 21:08

5 Answers5

6

Use BigInteger. It represents an arbitrarily large signed integer.

Adam
  • 2,422
  • 18
  • 29
  • 15
    Using `Tuple`, you can hold all values in scientific notation. – Phylogenesis Jul 29 '15 at 13:28
  • Or my answer is, use `decimal` class with 28 significant digits. – 9Deuce Jul 29 '15 at 13:42
  • I agree Phylo's is the best answer, but I read the title of the question, skimmed the question briefly and saw his question: "Does someone have a class or is there a system type for a really big number?" Thus my answer. We're all correct and regardless it's a duplicate. – Adam Jul 29 '15 at 13:45
  • 2
    @fubo "for which double is not accurate enough" doesn't necessarily mean OP wants to store fractions - `double` isn't accurate enough to store many large integers, either. – Rawling Jul 29 '15 at 13:46
  • 1
    @Rawling OP originally commented about "What about 0.000000001", but has since deleted that comment. – 9Deuce Jul 29 '15 at 13:53
  • 1
    @9Deuce I promise you I'm not disagreeing with you, just telling you where my answer came from and why I didn't mind decimals until you pointed it out. – Adam Jul 29 '15 at 13:55
  • @Adam I know. I think the comment stream got a little mixed up. I think there are a variety of different answers. – 9Deuce Jul 29 '15 at 14:00
  • It wasn't me who made a comment and deleted it. Saying that though, I do indeed need to store very small fractions too. That's why BigInteger doesn't suit my needs. – WonderWorker Jul 29 '15 at 14:15
  • You'd be better off going to find a 3rd party open source of a beta class called `BigRational` or research string math – Adam Jul 29 '15 at 14:16
  • @Knickerless-Noggins check out my answer with decimal. It holds a number of significant digits so it can hold a decently small fraction. – 9Deuce Jul 29 '15 at 15:07
  • 1
    @9Deuce: Decimal isn't accurate enough: Errors occur in the fractions. – WonderWorker Jul 29 '15 at 15:28
  • @Adam: I'll investigate BigRational. Thanks. – WonderWorker Jul 29 '15 at 15:29
  • @Knickerless-Noggins fair enough – 9Deuce Jul 29 '15 at 15:30
  • Why the downvote? Please explain your reasoning so I can improve my answer – Adam Jul 29 '15 at 15:38
  • BigInteger is only suitable for integers. I have editied the question to make it as idiot-proof as possible. -100000000 is not a small number. By small number I mean a characteristic of zero with a very long mantissa: As long as memory or even hard-drive will allow. – WonderWorker Jul 31 '15 at 11:53
  • @Phylogenesis I think you mean tuple. And it's true: A tuple can indeed represent all the numbers the spec requires. What's missing is the functionality, and I require the functionality of at least the type, 'double'. With the added precision and capacity of a tuple (or similar). – WonderWorker Jul 31 '15 at 13:22
  • @Knickerless-Noggins If you have numbers that require exponents outside the range of `int`, then yes, you will need a `Tuple`. I would counter that with the question "What on earth are you doing and can this be done a better way?" – Phylogenesis Aug 03 '15 at 07:38
  • What I'm doing is not being able to store Decimals correctly. As previously stated, tuple is missing the functionality, because the numbers need to be added, divided, multiplied, etc. What is needed are decimals as large as my computer will allow, sacrificing speed instead of precision. The Decimal data type's limit is ridiculously low at 79228162514264337593543950335, and furthermore, the fraction does not always store accurately. I can use the string datatype, but strings are limited in length by the int data type and also lack the functionality of Decimal data type. – WonderWorker Aug 03 '15 at 08:03
  • @Knickerless-Noggins Did `BigRational` or the concept of it help you at all? Or do you still have unanswered questions? – Adam Aug 03 '15 at 11:40
  • @Knickerless-Noggins Btw, `.0000000000000001 > -10000000000000` will return `true` ;) But I'll leave that out of the question. – Adam Aug 03 '15 at 11:41
  • It isn't good enough. I'm going to work on my own library, and if a better one comes along in the meantime then I'll use that. – WonderWorker Aug 03 '15 at 15:58
  • @Knickerless-Noggins Would this help you http://stackoverflow.com/questions/7887774/how-can-i-get-more-than-100-decimal-digits-in-c – Adam Aug 03 '15 at 18:31
3

It doesn't fulfill the spec, but I would use BigInteger for integers and decimal for decimals.

BigInteger (in theory) has no upper or lower bounds.

Decimal is precise up to 28 significant figures

WonderWorker
  • 8,539
  • 4
  • 63
  • 74
9Deuce
  • 689
  • 13
  • 27
  • Decimal isn't accurate enough: Errors occur in the fractions. Currently investigating BigRational suggested by Adam – WonderWorker Jul 29 '15 at 15:31
2

You may want to take a look at the BigRational class, which may be found on CodePlex.

It represents numbers as a ratio of two BigIntegers.

The ToString method doesn't meet your requirement as it formats the number as a ratio (numerator/denominator). But, since the source code is there, you can go wild implementing IFormattable.

OldFart
  • 2,411
  • 15
  • 20
1

You're asking for 2 things here that aren't currently possible in the same domain without losing some precision in your numbers. One thing is the infinity. And the other thing deals with the numerics. For example, unless you draw a line somewhere or you include some kind of pattern matching that tends towards a cutoff at a certain number, you cannot implement an "infinity".

But since you're going with really large numbers, and extremely precise small numbers, and since there is no known numeric data type that can support both, I recommend implementing your own. Just replicate the functionality of the numeric structures you already have and combine them to your needs.

My general idea is creating your own numeric data type that uses something like BigInteger for the integers and something like Decimals for the extremely precise numbers. Or you should also take a look at BigRational as they have some functionality for basic adding, subtracting, etc and they also have Numerator and Denominator properties.

The reason I'm going with Decimals over something else like Doubles is that Decimal types are fixed point and Doubles are floating point. Floating points are the reason why 0.1 + 0.2 = 0.30000000000000004, for example. But only consider this if you're completely implementing your own system.

tl;dr: Create your own class combining BigInteger and BigRational.

Kevin Hernandez
  • 522
  • 1
  • 6
  • 20
  • Also, apparently floating point calculations are faster than the Big* calculations. So consider looking into how much precision you actually need. http://stackoverflow.com/questions/5473136/can-c-sharp-store-more-precise-data-than-doubles – Kevin Hernandez Aug 14 '15 at 21:22
  • I only need infinity to be represented within the confines of human understanding. I.e. We know certain characteristics of infinity to be true e.g. multiples of infinity equal infinity and zero divided by infinity equal zero. We have huge harddrives these days, and I'd like that space used to store really big numbers. Infinity doesn't have to be stored in bit form when It can be a class with attributes and methods. To suggest that precision in large numbers ends at decimal on current computers is frankly ridiculous. – WonderWorker Mar 22 '16 at 08:40
1

For my learning purposes I created class that can manage large number of digits, use it if you want. This class has some issues and I don't guarantee that it works 100% (but i test it for many scenarios and for all it was ok). Also division operator has performance issues (try to decrease divPrecision for more speed) and I don't have time right now to learn about division algorithms. I am certain there is more sophisticated (and probably much better) way to make such class but it is what I built in couple of hours based on your idea.

using System;
using System.Linq;
using System.Numerics;

namespace RealNumber
{
    class RealNum
    {
        private BigInteger m = 0;
        private int w = 0;

        private static int divPrecision = 100000;
        private static char[] trimStartChar = { '0', '-' };
        private static char[] trimEndChars = { '.', ',' };

        public RealNum()
        {

        }

        public RealNum(BigInteger _m, int _w = 0 )
        {
            w = _w;
            m = _m;

            miniW();
        }

        public RealNum(string number)
        {
            number = number.Trim();

            System.Text.RegularExpressions.Regex textValidator = new System.Text.RegularExpressions.Regex(@"^-?[0-9]+([,.][0-9]+)?$");

            if (!textValidator.IsMatch(number))
            {
                throw new FormatException();
            }

            bool minSig = number.Contains('-');

            number = number.TrimStart(trimStartChar);
            if (number.Contains('.') || number.Contains(','))
            {
                number = number.TrimEnd(trimStartChar);
                number = number.TrimEnd(trimEndChars);
            }

            if (string.IsNullOrEmpty(number))
            {
                return;
            }

            char[] splitChars = { '.', ',' };

            string[] idnum = number.Split(splitChars, StringSplitOptions.None);
            if (string.IsNullOrEmpty(idnum[0]))
                idnum[0] = "0";

            if(idnum.Length==1)
            {
                m = BigInteger.Parse(idnum[0]);
            }
            else
            {
                w = idnum[1].Length;
                m = BigInteger.Parse(idnum[0]) * BigInteger.Pow(10, idnum[1].Length) + BigInteger.Parse(idnum[1]);
            }

            if (minSig)
                m = -m;

            miniW();

        }

        private void miniW()
        {
            while( m % (new BigInteger(10)) == 0 && m > 0 )
            {
                m = m / 10;
                w--;
            }
        }


        public override string ToString()
        {
            string num = m.ToString();

            if (w > 0)
            {

                if(num.Length - w <= 0)
                {
                    string zeros = new string('0', -num.Length + w + 1);
                    num = zeros + num;
                }

                num = num.Insert(num.Length - w, ".");
            }
            else if(w < 0)
            {
                string zeros = new string('0', -w);
                num = num + zeros;
            }

            return num;
        }


        public static RealNum operator+ (RealNum a, RealNum b)
        {

            int wSub = a.w - b.w;

            if(wSub<0)
            {
                wSub = -wSub;
                a = System.Threading.Interlocked.Exchange(ref b, a);
            }

            return new RealNum(a.m + b.m * BigInteger.Pow(10, wSub), a.w);

        }

        public static RealNum operator -(RealNum a, RealNum b)
        {
            int wSub = a.w - b.w;

            if (wSub < 0)
            {
                wSub = -wSub;
                a = System.Threading.Interlocked.Exchange(ref b, a);
                return new RealNum(b.m * BigInteger.Pow(10, wSub) - a.m, a.w);
            }

            return new RealNum(a.m - b.m * BigInteger.Pow(10, wSub), a.w);

        }

        public static RealNum operator *(RealNum a, RealNum b) => 
            new RealNum(a.m * b.m, a.w+b.w);

        public static RealNum operator /(RealNum a, RealNum b)
        {

            int precision = RealNum.divPrecision;
            if (precision <= b.w)
                precision = b.w+10;
            int aSubSup = 0;
            int aSub;
            if (a.w < 0)
            {
                aSubSup = -a.w;
                aSub = precision;
            }
            else
            {
                aSub = precision - a.w;
            }

            BigInteger am = a.m * BigInteger.Pow(10, aSubSup) * BigInteger.Pow(10, aSub);

            return new RealNum(am/b.m, precision-b.w);

        }

    }
}
Logman
  • 4,031
  • 1
  • 23
  • 35