zoukankan      html  css  js  c++  java
  • 用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

  • 相关阅读:
    C#跨窗体操作
    搞IT的不如去养鸡养猪了
    C# 委托实例(跨窗体操作控件)
    FastReport 自定义页长
    SQL 根据一个表更新另一个表的内容
    Delphi中用ADOQuery实现主从表的例子(转)
    旅行的意义
    嘉州影院的网址
    纯粹的人
    Delphi中流的使用:压缩与解压缩(TCompressionStream、TDecompressionStream)
  • 原文地址:https://www.cnblogs.com/Charltsing/p/BigDecimal.html
Copyright © 2011-2022 走看看