分享一个博主碰到的问题。
    1、double类型保留小数问题
    
    当时有一段代码来处理小数位数,例如这样,
   
            object objValue = "9.1234567890123456";
             Double.TryParse(objValue.ToString(), out var dValue);
            string result = Math.Round(dValue, 15).ToString();
    对于输入的objValue,做一个小数点位数的保留。
    
    比如这里的输出是这样,Console.WriteLine(result);
   
     
   
0-15位小数都没问题。
    但是问题来了,当我想保留大于15位小数时,Math.Round方法不行了,
    
    上面代码,如果
    
    string result = Math.Round(dValue, 16).ToString();
    
    会报这个错:
    
    System.ArgumentOutOfRangeException:“舍入位数必须在 0 和 15 之间(包括 0 和 15)。
   
    2、decimal处理大于15位的小数
    
    double类型只能保留15位小数,于是准备用decimal解决。
   
            string stringValue = "9.12345678901234567890";
            string result =  decimal.Round(decimal.Parse(stringValue), 18).ToString();
    
    输出Console.WriteLine(result),
   
     
   
如果大家碰到了double解决不了的小数问题,可以使用decimal来解决。
    3、小数转string精度丢失问题
    
    事情到此结束了吗?
    
    对于博主来说才刚刚开始,因为,对于数据的处理只是其中一个功能的一小段代码,前面提过我的源数据是object类型的,并不是string类型。
    
    于是转换一下就好了,
   
object objValue = 9.1234567890123456789;
string stringValue = objValue.ToString();输出后Console.WriteLine(“object类型9.1234567890123456小数转string后:” + stringValue),
     
   
    
    我尝试了Convert,对objValue加字符串操作,StringBuilder产生字符串,都不行,精度就是会丢失。究其原因,因为object objValue = 9.1234567890123456789赋值时编译器就把这个值当作double处理了,对于double超过15位的小数值double是不认识的。
   
    4、取整数处理多位小数转换时精度丢失问题
    
    于是,对于object类型的超过15位小数如何正常转换过来呢,
    
    我准备将小数转为整数,处理代码如下,
    
   
        //源数据
            int dnumber = 16;
            object abs = 2.1234567890123456;
            
            //处理
            long dec = (long)(Math.Pow(10, dnumber) * (double)abs);
            string dbstr = dec.ToString();
            char[] cstr = dbstr.ToCharArray();
            string formatValue = string.Empty;
            string formatInt = string.Empty;
            string formatDouble = string.Empty;
            int length = cstr.Length;
            string intNumber = Convert.ToInt32(abs).ToString();
            int intLength = intNumber.Length;
            for (int i = 0; i < length; i++)
            {
                if (i < intLength)
                {
                    formatInt += cstr[i];
                }
                else
                {
                    formatDouble += cstr[i];
                }
            }
            if (!string.IsNullOrEmpty(formatInt) && !string.IsNullOrEmpty(formatDouble))
            {
                formatValue = formatInt + "." + formatDouble;
            }
            decimal d = decimal.Round(decimal.Parse(formatValue), dnumber);
            string strDec = d.ToString(string.Format("f{0}", dnumber));输出,Console.WriteLine(strDec),
     
   
    这样,将object小数无损转为decimal类型。
    
    但是这里有一个弊端,long类型毕竟长度有限,如果源小数太大,就会因为long的限制,程序报错。
    
    这里我也尝试了使用如下这个方法处理小数和整数部分,但是,取整数时不能无损取出
   
   private  static string FormatDoubleNumber(object value, int number)
        {
            string result = value.ToString();
            try
            {
                //取小数的整数部分
                int intValue = Convert.ToInt32(value);
                double decValue = (double)value % 1;
                
                long longValue = (long)(Math.Pow(10, number) * (double)decValue);
                string dbstr = longValue.ToString();
                char[] cstr = dbstr.ToCharArray();
                string formatValue = string.Empty;
                string formatInt = intValue.ToString();
                string formatDouble = string.Empty;
                int length = cstr.Length;
                for (int i = 0; i < length; i++)
                {
                    formatDouble += cstr[i];
                }
                if (!string.IsNullOrEmpty(formatInt) && !string.IsNullOrEmpty(formatDouble))
                {
                    formatValue = formatInt + "." + formatDouble;
                }
                decimal decimalValue = decimal.Round(decimal.Parse(formatValue), number);
                result = decValue.ToString(string.Format("f{0}", number));
            }
            catch { }
            return result;
        }
    这个方法中,(double)value % 1就已经丢失精度了。
    
   
    5、最终并没有实际解决的解决办法
    
    受限于传入的类型是object,所以无法无损精度的转换,只能牺牲一些精度,但是可以保持小数长度。
    
    使用System.ComponentModel下的DoubleConverter来处理object类型的小数,
   
           object dd = 2.1234567890123456;
            DoubleConverter dcb = new DoubleConverter();
            string strdc = dcb.ConvertToInvariantString(dd);
            decimal decimalValue = decimal.Round(decimal.Parse(strdc), 16);
            string rd = decimalValue.ToString(string.Format("f{0}", 16));输出,Console.WriteLine(rd),
     
   
这样能勉强保留小数的长度(大于15位)
其实对于超过15位的小数在C#中最好使用decimal,但是在数据传入时最好时string类型,使用比如double类型就会造成精度丢失。
    演示代码下载:
    
     https://download.csdn.net/download/yysyangyangyangshan/13985465
    
    
   
 
