怎樣避免C#中將小數轉換為字符串時出現科學記數法


在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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM