C#中令人难以置信的巨大且难以置信的小数字(不仅仅是整数)

时间:2015-07-29 13:23:23

标签: c# class math

在C#中,我需要使用非常大(和非常小)的数字,其中decimal和double不够准确,BigInteger无法存储数字的分数。

我想让数字具有尽可能长的组件,即特征和尾数,因为内存(最好是硬盘驱动器)空间允许。

是否有人有类或者系统类型是否真的很大。

我需要能够加,减,除,模,平方,平方根,sin,cos,tan(和它们的反转)并乘以数字。几乎是标准十进制/双精度的完整功能(如果我错过了任何一个)。

无穷无需代表,但这将是一个加号*!

一个非常小的数字的例子是:

0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001

以及非常大的数字的例子是:

1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001

-1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001

我更喜欢ToString()以上述形式返回数字。科学记数法是可以接受的,但绝不是首选。

四个最重要的要求是:

  1. 数字的准确性
  2. 至少,基本的数学运算可以 应用;乘法,除法,加法和减法
  3. 该号码必须仅限于备用内存和硬盘的大小。
  4. Number必须以表单中的等效字符串形式输出 特征,小数点,然后是尾数,例如100.23,100或 0.000000054
  5. 应该支持尾数中的复发
  6. BigInteger不是一个可接受的答案。

    *如果无穷无尽,那么我只需要尽可能地实现它,例如(无穷大/无穷大= 1),(0 /无穷大= 0)等。

5 个答案:

答案 0 :(得分:6)

使用BigInteger。它代表一个任意大的有符号整数。

答案 1 :(得分:3)

它不符合规范,但我会将BigInteger用于整数,将decimal用于小数。

BigInteger(理论上)有no upper or lower bounds.

十进制精确到28 significant figures

答案 2 :(得分:2)

您可能需要查看可能在CodePlex上找到的BigRational类。

它将数字表示为两个BigIntegers的比率。

ToString方法不符合您的要求,因为它将数字格式化为比率(分子/分母)。但是,由于源代码存在,你可以疯狂地实现IFormattable。

答案 3 :(得分:1)

你在这里要求的两件事在同一领域目前是不可能的,而你的数字并没有丢失一些精确度。有一点是无限。另一件事涉及数字。例如,除非你在某处画一条线或者包含某种类型的模式匹配,这种模式匹配倾向于某个数字的截止,否则你无法实现“无穷大”。

但是,由于你的数字非常大,而且数字非常精确,并且由于没有可以同时支持的已知数字数据类型,我建议你自己实现。只需复制您已有的数字结构的功能,并将它们与您的需求结合起来。

我的一般想法是创建自己的数值数据类型,对于整数使用类似BigInteger的数字类型,对于非常精确的数字,使用类似Decimals的类似数据。或者你也应该看一下BigRational因为它们具有一些基本的加法,减法等功能,而且它们也有Numerator和Denominator属性。

我使用Decimals而不是Doubles之类的其他原因是Decimal类型是固定点而Doubles是浮点数。例如,浮点是0.1 + 0.2 = 0.30000000000000004的原因。但只有在你完全实现自己的系统时才考虑这个。

tl; dr:创建自己的类,结合BigInteger和BigRational。

答案 4 :(得分:1)

为了我的学习目的,我创建了可以管理大量数字的类,如果需要可以使用它。这个课有一些问题,我不保证它100%工作(但我在很多场景中测试它,并且一切都没问题)。除法运算符也有性能问题(尝试降低divPrecision以获得更快的速度),我现在还没有时间去了解除法算法。我确信有更复杂(也可能更好)的方法来制作这样的课程,但这是我根据你的想法在几个小时内建立的。

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);

        }

    }
}