请选择 进入手机版 | 继续访问电脑版

莫名奇妙的异常008:C#中多位小数问题

[复制链接]
孤单 发表于 2020-12-31 18:57:28 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
分享一个博主遇到的问题。
1、double范例生存小数问题
其时有一段代码来处置惩罚小数位数,比方这样,
  1.             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管理。
  1.             string stringValue = "9.12345678901234567890";            string result =  decimal.Round(decimal.Parse(stringValue), 18).ToString();
复制代码
            
输出Console.WriteLine(result),

如果大家遇到了double管理不了的小数问题,可以使用decimal来管理。
3、小数转string精度丢失问题
事情到此竣事了吗?
对于博主来说才刚刚开始,因为,对于数据的处置惩罚只是其中一个功能的一小段代码,前面提过我的源数据是object范例的,并不是string范例。
于是转换一下就好了,
  1. 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位小数如何正常转换过来呢,
我准备将小数转为整数,处置惩罚代码如下, 
  
  1.         //源数据            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的限制,步伐报错。
这里我也实验了使用如下这个方法处置惩罚小数和整数部分,但是,取整数时不能无损取出
  1.    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范例的小数,
  1.           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
           

来源:https://blog.csdn.net/yysyangyangyangshan/article/details/111991588
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

发布主题

专注素材教程免费分享
全国免费热线电话

18768367769

周一至周日9:00-23:00

反馈建议

27428564@qq.com 在线QQ咨询

扫描二维码关注我们

Powered by Discuz! X3.4© 2001-2013 Comsenz Inc.( 蜀ICP备2021001884号-1 )