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

  • 相关阅读:
    【Azure 应用服务】在Azure App Service多实例的情况下,如何在应用中通过代码获取到实例名(Instance ID)呢?
    【Azure 应用服务】App Service For Windows 中如何设置代理实现前端静态文件和后端Java Spring Boot Jar包
    【Azure Developer】使用Azure Key Vault 的Key签名后,离线验证的一些参考资料
    【Azure Function】调试 VS Code Javascript Function本地不能运行,报错 Value cannot be null. (Parameter 'provider')问题
    【Azure 应用服务】App Service 使用Tomcat运行Java应用,如何设置前端网页缓存的相应参数呢(Xms512m Xmx1204m)?
    【Azure API 管理】APIM添加Logtoeventhub的策略后,一些相关APIM与Event Hub的问题
    【Azure API 管理】为调用APIM的请求启用Trace 调试APIM Policy的利器
    【Azure 事件中心】China Azure上是否有Kafka服务简答
    【Azure 应用服务】探索在Azure上设置禁止任何人访问App Service的默认域名(Default URL)
    【Azure 微服务】记一次错误的更新Service Fabric 证书而引发的集群崩溃而只能重建
  • 原文地址:https://www.cnblogs.com/Charltsing/p/BigDecimal.html
Copyright © 2011-2022 走看看