用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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM