在C#中如果float、double、decimal類型的值,小數點后的0太多時,C#會用科學記數法來表示小數的值。
例如下面的double類型0.00009,如果我們直接將其用ToString()方法轉換為字符串,就會變為科學記數法9E-05
double number = 0.00009; string defaultNumber = number.ToString(); //9E-05
此外如果float、double、decimal類型的值只有整數位,且整數后面有很多0,C#也會用科學記數法來表示整數的值,例如下面的double類型210000000000000000,如果我們直接將其用ToString()方法轉換為字符串,就會變為科學記數法2.1E+17
double number = 210000000000000000; string defaultNumber = number.ToString(); //2.1E+17
所以我們可以通過顯式聲明轉換后字符串的格式,避免在C#中出現科學記數法:
using System; namespace NetCoreFloat { class Program { static void Main(string[] args) { double number = 0.00009; string defaultNumber = number.ToString(); //9E-05 string numberFromToString = number.ToString("N5"); //0.00009 string numberFromStringFormat = string.Format("{0:F5}", number); //0.00009 Console.WriteLine("Press any key to end..."); Console.ReadKey(); } } }
雖然上面的代碼可以讓小數轉換為字符串后不是科學記數法,但是很明顯我們需要准確地知道小數點后會有多少位小數,才能保證轉換后的精度不會丟失。
如果我們將轉換格式的精度設置得過小,就會造成精度損失,如下所示:
using System; namespace NetCoreFloat { class Program { static void Main(string[] args) { double number = 0.00009; string defaultNumber = number.ToString(); //9E-05 string numberFromToString = number.ToString("N3"); //0.000 string numberFromStringFormat = string.Format("{0:F3}", number); //0.000 Console.WriteLine("Press any key to end..."); Console.ReadKey(); } } }
而如果我們將轉換格式的精度設置得過大,又會在小數最后產生多余的0,如下所示:
using System; namespace NetCoreFloat { class Program { static void Main(string[] args) { double number = 0.00009; string defaultNumber = number.ToString(); //9E-05 string numberFromToString = number.ToString("N10"); //0.0000900000 string numberFromStringFormat = string.Format("{0:F10}", number); //0.0000900000 Console.WriteLine("Press any key to end..."); Console.ReadKey(); } } }
對此我們需要在轉換格式中使用#字符,#字符不會在小數最后產生多余的0,如下所示:
using System; namespace NetCoreFloat { class Program { static void Main(string[] args) { double number = 0.00009; string numberFromToString = number.ToString("0.#####"); //0.00009 numberFromToString = number.ToString("0.##########"); //0.00009 number = 100.00009; numberFromToString = number.ToString("0.#####"); //100.00009 numberFromToString = number.ToString("0.##########"); //100.00009 number = 210000000000000000; numberFromToString = number.ToString("0.#####"); //210000000000000000 numberFromToString = number.ToString("0.##########"); //210000000000000000 Console.WriteLine("Press any key to end..."); Console.ReadKey(); } } }
實際上我們可以最大聲明339個#字符,這樣可以保證所有小數都能被正確地轉換為字符串:
using System; namespace NetCoreFloat { class Program { static void Main(string[] args) { double number = 0.00009; string numberFromToString = number.ToString("0." + new string('#', 339));//0.00009 Console.WriteLine("Press any key to end..."); Console.ReadKey(); } } }
我們也可以聲明一個類FormatStrings,將339個#字符聲明為一個字符串常量DoubleFixedPoint,這樣用起來也會更方便:
using System; namespace NetCoreFloat { public static class FormatStrings { public const string DoubleFixedPoint = "0.###################################################################################################################################################################################################################################################################################################################################################"; } class Program { static void Main(string[] args) { double number = 0.00009; string numberFromToString = number.ToString(FormatStrings.DoubleFixedPoint);//0.00009 Console.WriteLine("Press any key to end..."); Console.ReadKey(); } } }
C#浮點數的精度問題
我們這里就拿double類型來舉例,float和decimal類型以此類推, double的最大值是(double.MaxValue):
179769313486232000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
其中有15位非0數字,也就是前面的179769313486232,那么它表示double類型可以存儲整數位加小數位一共15位的有效數字(也就是說double類型中,第1位和最后1位非0的數字,一共能有15位)。
例如如果我們聲明一個18位的小數number,然后通過ToString方法將其輸出為字符串:
double number = 1234567890.12345678;//整數10位,小數8位 string numberFromToString = number.ToString("0." + new string('#', 339));//1234567890.12346
可以看到ToString方法輸出的是1234567890.12346,整數位加小數位一共只有15位,其中最后一位小數數字是6,是因為被#字符四舍五入了。
現在我們提高整數位的位數到13位,聲明一個一共21位的小數,我們看看結果如何:
double number = 1234567890123.12345678;//整數13位,小數8位 string numberFromToString = number.ToString("0." + new string('#', 339));//1234567890123.12
可以看到這次最后ToString方法輸出的是1234567890123.12,整數位加小數位還是一共只有15位,而這次由於整數位占用了15位中的13位數字,所以小數位只剩下2位數字。
接下來我們聲明一個一共20位的整數給double類型,看看結果如何:
double number = 12345678901231211111;//整數20位 string numberFromToString = number.ToString("0." + new string('#', 339));//12345678901231200000
可以看到這次最后ToString方法輸出的是12345678901231200000,原因就是因為double類型最大只能存儲15位有效數字,所以最后5位1被截斷了變為了0,只剩下了前面15位有效數字。
參考文獻:
Converting numbers to strings without scientific notation in C#
Double to string conversion without scientific notation