iT邦幫忙

3

[學習筆記]數值運算的陷阱,為什麼與計算機按出來的不一樣?

我們一般使用的數字型態不外乎就是int,float,double
但有關金錢的運算就需要更準確的decimal型態了

先來說一下為何不使用double呢?

大家可以試試下面這個例子

// 0.30000000000000004
double a = 0.1 + 0.2;

float,double : 二進制浮點數
decimal : 十進制浮點數

會造成這個原因是因為電腦運算double會轉成二進位去做運算
而小數在二進制很難完整表示,就像我們算1/3一樣

所以我們需要使用更精確的decimal型態來做小數運算

// 0.3
decimal b = 0.1m + 0.2m;

Tostring造成計算誤差

雖然前面說數值運算要用decimal
但有時還是難免會使用到double型態
像是要計算複利時,會呼叫Math.Pow這個double funtion

//1.0221044505936159
double rate = Math.pow(1.3,1.0/12.0);
//1.02210445059362
decimal drate = decimal.Parse(rate.Tostring());

這時發現tostring預設會四捨五入至小數點後14位
因為是在算複利,所以在幾十年後會出現誤差,雖然差值很小但也不容忽視

這時只要給Tostring下G17(double格式)參數就可以保留位數了

MSDN

根據預設,傳回的值只包含 15 個位數的精確度雖然內部維護最多 17 個位數。 如果這個執行個體的值必須大於 15 位數,ToString會傳回PositiveInfinitySymbol或NegativeInfinitySymbol而不是預期的數目。 如果您需要更多有效位數時,指定format"G17"格式規格,它一定會傳回 17 個位數的有效位數或"R",它會傳回 15 位數如果數目可以使用 17 位數來表示,如果只能是數字表示使用最大有效位數。

//1.0221044505936159
decimal drate = decimal.Parse(rate.Tostring("G17"));

這種小數點後10幾位的誤差真的很難發現
/images/emoticon/emoticon13.gif
但牽扯到金融相關程式
還是必須排除可能的誤差


2 則留言

0
grtert
iT邦新手 5 級 ‧ 2019-06-26 09:29:21

學到了,謝謝分享!

0
小魚
iT邦高手 1 級 ‧ 2019-06-29 17:15:53

這篇不錯,
我倒是沒去研究過這個原理!

我要留言

立即登入留言