1. 程式人生 > >float,double,decimal彼此轉換會出現的精度問題(今天你Bug了嗎?)

float,double,decimal彼此轉換會出現的精度問題(今天你Bug了嗎?)

在一開始先說明下,此處的精度問題並不包含數字過大、強轉溢位之類導致的問題,主要說明三者之間比較或計算時容易產生的精度問題,以避免開發中莫名其妙的資料問題。
大學時學的是C語言,雖然就沒學會過,但對於float和double的描述還是記的很清楚,float和double因為採用科學記數法,會出現精度問題,要比較的話,應該是用兩個的差值,取絕對值,如果該值小於某個數,你就可以認為兩個數字是相等的,比如下面的寫法

if (Math.Abs(1.01f - 1.01d) < 0.001)
{
   //相等
}
而下面這個是永遠不會為true
if (1.01f == 1.01f)
{ //永遠都不可能進入此塊區域
}
結果在C#裡上面的==比較返回結果卻是True,而且聲明瞭1.01f,直接用的時候,還是1.01,居然沒變!(這裡不知道我是不是記錯了,也不可能在找C來嘗試了)然後在(double)1.01f時,輸出卻不是1.01了,所以在這裡做了一個完整的測試,輸出結果直接備註在了後面(不同電腦上的精度值可能有所不同)
            float x = 100.1f;
            double y = 100.1d;
            decimal z = 100.1m;
            Console.WriteLine("X:" + x + " Y:" + y + " Z:" + z);//X:100.1 Y:100.1 Z:100.1
            Console.WriteLine("Double X:" + (double)x + " Float Y:" + (float)y + " Double Z:" + (double)z + " Float Z:" + (float)z);//Double X:100.099998474121 Float Y:100.1 Double Z:100.1 Float Z:100.1
            Console.WriteLine("Float To Double To Float: " + (float)((double)x));//Float To Double To Float: 100.1
            Console.WriteLine("Double To Float To Double: " + (double)((float)y)); //Double To Float To Double: 100.099998474121
            Console.WriteLine("(Decimal)Float Equals (Decimal)Double: " + ((decimal)x == (decimal)y));//(Decimal)Float Equals (Decimal)Double: True
            Console.WriteLine("(Double)Decimal Equals (Float)Decimal: " + ((double)z == (float)z));//(Double)Decimal Equals (Float)Decimal: False
            Console.WriteLine("Double Equals Float: " + (x == y));//Double Equals Float: False
            Console.WriteLine("Double Equals Float Transfer: " + ((double)x == y));//Double Equals Float Transfer: False
            Console.WriteLine("Float Equals Double Transfer: " + (x == (float)y));//Double Equals Float Transfer: True
            Console.WriteLine("Float Equals Decimal Transfer: " + (x == (float)z));//Float Equals Decimal Transfer: True
            Console.WriteLine("Double Equals Decimal Transfer: " + (y == (double)z));//Double Equals Decimal Transfer: True
            var a = 100.111d * x;
            var b = 100.111d * y;
            var c = 100.111d * y;
            var d = 100.111f * x;
            var e = 100.111f * x;
            Console.WriteLine("A:" + a + " B:" + b + " C:" + c + " D:" + d + " E:" + e);//A:10021.1109472427 B:10021.1111 C:10021.1111 D:10021.11 E:10021.11
            Console.WriteLine("Double*Float Equals Double*Double: " + (a == b));//Double*Float Equals Double*Double:  False
            Console.WriteLine("Double*Double Equals Double*Double: " + (b == c));//Double*Double Equals Double*Double: True
            Console.WriteLine("Float*Float Equals Float*Float: " + (e == d));//Float*Float Equals Float*Float: True
            Console.WriteLine("Double*Double Equals Float*Float: " + (b == d));//Double*Double Equals Float*Float: False

在上面的例子中同時對float、double、decimal做了彼此之間的轉化與輸出,在這三個型別中,彼此之間允許從低向高進行隱式轉換是:float-->double ,從高向低必須顯式轉換(備註:float和double不能隱式轉換成decimal,必須顯式轉換)

C#預設實現了隱式轉換功能,所以float==double判斷與(double)float==double其實是一致的,同樣的情況會出現在加減乘除等地方,但通過上面的demo,可以很明確的得到結論:

1、相同型別的數字進行比較,不存在隱式轉換,值也就不會變化,所以可以得到True的結果

2、如果型別不同進行比較時,float轉double會出現精度丟失,但double轉float不會出現精度丟失(甚至會補上精度,神奇的(float)((double)x)結果還是100.1)

3、float-->decimal,double-->decimal不會出現精度問題,反過來也不會出現精度問題

所以騷年,請不要在程式裡面直接進行float==double的比較,這看起來一樣的數字,結果一定會是false,如果你真要進行比較,請都強轉成decimal或者將double強轉成float再進行比較