用C# BigInteger实现的BigDecimal类,终于可以直接做四则运算了。

https://code.google.com/p/dotnet-big-decimal/

这是个BigDecimal类的开源项目,支持Operators +, - and *

俺给改了改,加上了除法。原来的BigDecimal项目没有整数部分的长度属性,所以没有直接用BigInteger来实现除法,而是自己处理字符串,同时由于.Net4.0的BigInteger类 Parse和ToString方法效率极低,导致这个类效率不高,只能凑合用。

        public static BigDecimal operator /(BigDecimal left, BigDecimal right)
        {
            var scale = Math.Max(left.scale,  right.scale);
            string decimal1 = left.ToString();
            string decimal2 = right.ToString();

            BigDecimal value = BigDecimal.Parse(Division(decimal1, decimal2, scale));
            return value;
        }


        /// <summary>
        /// 计算除法,精度由CustomScale决定,超过部分直接截去。如果CustomScale小于0,表示精度由前两个参数的最长小数位决定。
        /// </summary>   
        private static string Division(string decimal1, string decimal2, int CustomScale)
        {
            //算法:把两个参数的小数部分补0对齐,整数部分确保被除数长度大于除数,
            //      放大被除数的要求精度的倍数,确保整数计算能保留希望的小数部分
            //      还原小数点,输出要求的精度,多余部分截断
            if (!isNumeric(decimal1)) throw new ArgumentException("Invalid argument");
            if (!isNumeric(decimal2)) throw new ArgumentException("Invalid argument");

            //判断负号
            int s1 = decimal1.IndexOf('-');
            if (s1 >= 0) decimal1 = decimal1.Replace("-", "");

            //判断负号
            int s2 = decimal2.IndexOf('-');
            if (s2 >= 0) decimal2 = decimal2.Replace("-", "");

            int sign = s1 + s2;     //=-2都是负数;=-1一正一负;=0都是正数;>0非法数字

            int decimalpartlength1 = 0;
            int decimalpartlength2 = 0;
            int integerpartlength1 = 0;
            int integerpartlength2 = 0;

            int maxscale = 0;
            BigInteger bi1;
            BigInteger bi2;

            //检查小数部分长度
            int pointIdx1 = decimal1.IndexOf('.');
            if (pointIdx1 >= 0)
            {
                decimalpartlength1 = decimal1.Length - pointIdx1 - 1;      //得到小数部分长度
                integerpartlength1 = pointIdx1 == 0 ? 1 : pointIdx1;       //得到整数部分长度,考虑小数点在第一位的情况
            }
            else
            {
                integerpartlength1 = decimal1.Length;                      //得到整数部分长度
            }

            //检查小数部分长度
            int pointIdx2 = decimal1.IndexOf('.');
            if (pointIdx2 >= 0)
            {
                decimalpartlength2 = decimal2.Length - pointIdx2 - 1;      //得到小数部分长度
                integerpartlength2 = pointIdx2 == 0 ? 1 : pointIdx2;       //得到整数部分长度,考虑小数点在第一位的情况
            }
            else
            {
                integerpartlength2 = decimal2.Length;                      //得到整数部分长度
            }

            decimal1=decimal1.Replace(".", "");
            decimal2=decimal2.Replace(".", "");

            //对齐小数部分
            if (decimalpartlength1 < decimalpartlength2)
            {
                decimal1 = decimal1 + new string('0', decimalpartlength2 - decimalpartlength1);
            }
            if (decimalpartlength2 < decimalpartlength1)
            {
                decimal2 = decimal2 + new string('0', decimalpartlength1 - decimalpartlength2);
            }

            bi1 = BigInteger.Parse(decimal1);
            bi2 = BigInteger.Parse(decimal2);

            if (bi2.ToString() == "0") throw new DivideByZeroException("DivideByZeroError");  //throw new DivideByZeroException("DivideByZeroError")

 
            int rightpos = 0;                                               //计算从右边数小数点的位置,用于还原小数点
            int pows = integerpartlength2 - integerpartlength1;
            if (pows >= 0)
            {
                bi1 = bi1 * BigInteger.Pow(10, pows + 1);                   //放大被除数,确保大于除数
                rightpos += pows + 1;
            }

            //确定小数位的精度
            maxscale = Math.Max(decimalpartlength1, decimalpartlength2);
            if (CustomScale < 0)
            {
                CustomScale = maxscale;                                     //CustomScale<0,表示精度由参数决定
            }
            else
            {
                maxscale = Math.Max(maxscale, CustomScale);                 //得到最大的小数位数
            }

            bi1 = bi1 * BigInteger.Pow(10, maxscale);             //放大被除数,确保整数除法之后,能保留小数部分
            rightpos += maxscale;

            BigInteger d = bi1 / bi2;                                       //注意整数除法的特点:会丢掉小数部分
            string result = d.ToString();

            if (rightpos > result.Length)
            {
                result = "0." + new string('0', rightpos - result.Length) + result;    //小数点后面的0补上,再还原小数点
            }
            else
            {
                result = result.Insert(result.Length - rightpos, ".");                 //还原小数点
                if (result.StartsWith(".")) result = "0" + result;                     //补上个位的0
            }

            //超出精度截断
            if (rightpos > CustomScale) result = result.Substring(0, result.Length - (rightpos - CustomScale));
            //还原正负号
            if (sign == -1) result = "-" + result;
            return result;
        }

        /// <summary>
        /// 判断字符串是不是数字:不能有两个小数点、负号只能在最前面、除了小数点和负号,只能是数字。
        /// </summary>        
        private static bool isNumeric(string strInput)
        {
            char[] ca = strInput.ToCharArray();
            int pointcount = 0;
            for (int i = 0; i < ca.Length; i++)
            {
                if ((ca[i] < '0' || ca[i] > '9') && ca[i] != '.' && ca[i] != '-') return false;
                if ((ca[i] == '-') && (i != 0)) return false;

                if (ca[i] == '.') pointcount++;
            }
            if (pointcount > 1) return false;
            return true;
        }

 

 

如果需要快速的大数类,可以看看这个http://www.cnblogs.com/skyivben/archive/2008/07/25/1251697.html,

下载链接  https://bitbucket.org/ben.skyiv/biginteger

 

posted on 2013-11-30 14:26 Charltsing 阅读(...) 评论(...) 编辑 收藏

导航