分享一个博主遇到的问题。
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
来源:https://blog.csdn.net/yysyangyangyangshan/article/details/111991588
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |