浮點數運算

常見

浮點數表達方式

之前大家應該有在前面的章節看到過 floatdouble,這兩個資料型態都是拿來存浮點數的!那要如何表達一個浮點數呢?以下以 double 為例:

double a = 0.0025;

以數學寫法表示是一種方法,而另外一種則是科學記號:

double a = 2.5e-3;
補充

大數也可以用科學記號,之後用各種演算法的時候常常會需要訂一個常數作為最大值,這時候就可以用科學記號,會比打很多個零之後還要慢慢算有幾個零還要快很多。但是要注意科學符號表示法的資料型態是浮點數,要用 int 強制轉型,要不然拿來宣告陣列長度之類的會出事。

const int N = 1e6; // 1000000

除法要注意

浮點數的加、減、乘法都跟整數一樣,但是除法好像不太一樣?!

cout << 5 / 2 << '\n';
cout << 5.0 / 2.0 << '\n';

這兩個輸出看起來似乎沒有差別,但是執行後會發現一個是 2,一個是 2.5,這是因為整數運算中 / 的答案會被自動無條件捨去,但浮點數運算則會保留小數點。

輸出的位數

有的時候題目會指定你輸出某個小數到小數點第 2 位之類的,那要怎麼辦呢? C++ 有一個函式叫做 setprecision()

cout << fixed << setprecision(2) << 4.0 / 3.0; //1.33

其中,後面括號放的是要保留的位數(以上面的例子來說就是保留 2 位),這個設定會持續到後面的每一筆輸出,所以如果要改的話要重新設定。

fixed 是幹什麼的呢?在一些情況下,C++ 會輸出包含科學記號的數字,這時候 fixed 就是用來關閉科學記號的。

浮點數誤差

當你嘗試將上面舉的例子多輸出幾位時你會發現一些奇怪的事:

cout << fixed << setprecision(20)  << 4.0 / 3.0;

你會發現輸出是 1.33333333333333325932 而不是 1.33333333333333333333,這是因為浮點數的精度有限,所以在計算的時候會有一點誤差,關於電腦裡儲存浮點數的方式有興趣的可以參考 這篇文章。而 doublefloat 的差別正是 double 能存的精準位數比較多。

那要怎麼解決呢?

  • 想辦法避掉浮點數:這個方法能用的範圍有點有限,要題目條件剛好能用才行,好比說這題,所有小數皆包含個位數 0 及到小數點後第 9 位,這代表這些小數真正有差的只有小數部份,而解題過程會用到的運算也不需要真的把所有數字都當作小數處理,所以我們可以把他們的小數部分當作一般數字看待就好 ( 例如 0.000000012 當 12 ),可以完美避開浮點數加法帶來的誤差。
  • 分數:有點複雜,但保證精準。好比說像這題看似解題要算斜率,絕對不要真的把斜率算出來然後弄出一個直線方程式,應該一個一個點慢慢用分數算 x, y 比例 ( 其實就是斜率 )。
  • 非用不可的時候:使用誤差容忍值( eps ),要判斷相等的時候,只要b - eps < a && a < b + eps就可以了,比大小同理可證。
注意

前面教過的一些數學函式如 powsqrt 等也都牽涉浮點數運算,所以也要處理誤差!

小測驗

程式 cout << 1.0 + 2.0; 會輸出什麼結果?