四捨五入相信網友一定在求學過程中接觸過,但是否曾經在程式中使用到呢?
記得以前接觸VB時,似乎沒有四捨五入的函式可供使用者使用,而必須自己撰寫,
現在作者介紹一些基本的概念。
假設一個數字10.6,要怎麼讓電腦知道四捨五入呢?兩個步驟
(1)加0.5 = 11.1
(2)取整數 = 11
若假設數字10.65,現在要四捨五入到小數點第一位:
(1)乘於10 = 106.5
(2)加0.5 = 107.0
(3)取整數 = 107
(4)除於10 = 10.7
若設數字10.653,現在要四捨五入到小數點第二位:
(1)乘於100 = 1065.3
(2)加0.5 = 1065.8
(3)取整數 = 1065
(4)除於100 = 10.65
::::::::::::::::::::::::::::::::::::::::::::::
#include<iostream>
using namespace std;
double round(double num, unsigned precision)
{
double pow = 1.0;
for(int i=0; i < precision; ++i)
pow *= 10;
if(num > 0) //正數
return int(num * pow + .5) / pow;
return int(num * pow - .5) / pow; //負數
}
void main()
{
cout<<"10.563取四捨五入到小數點第二位:"
<< round(10.563,2) <<endl;
cout<<"10.563取四捨五入到小數點第一位:"
<< round(10.563,1) <<endl;
cout<<"10.563取四捨五入到整數:"
<< round(10.563,0) <<endl;
}
::::::::::::::::::::::::::::::::::::::::::::::
[說明]
函式有兩個參數num與precision, precision(精度)意指要四捨五入到小數點第幾位,
0表示四捨五入到整數部份,1表示四捨五入到小數點第一位,以此類推;初學者比較有
問題的應該是在return的部份:
return int(num * pow + .5) / pow;
我一一將它分解說明:
(1)num * pow
=>這同等於文章一開始說明的步驟1
(2)num * pow + .5
=>加0.5
(3)int(num * pow + .5)
=>取整數,這是型態轉換的語法
(4)int(num * pow + .5) / pow
=>這同等於文章一開始說明的步驟4
※注意此處int(),在作者使用的VC中這個int的型態是指signed long int,大小為4byte
signed long int的最大值為2147483647;最小值為-2147483648,這兩個數值是我接著
要用到的。
如果語法不熟悉的地方,請重新翻閱相關書籍,複習一下馬上再回來吧!!
round這個函數夠嚴謹了嗎?適用於任何的數值嗎?
那你試試這個兩個數值看看:2147483647.5與-2147483648.5
試出來的結果應該是這樣-2.14748e+009與-2.14748e+009為什麼是負的呢?
答案是溢位(Overflow)了!!!where發生溢位呢?發生溢位的地方在return的地方
說明一下:
return int(num * pow + .5) / pow;
int(num * pow + .5)這個動作是做轉換的動作,將double轉成int,在轉換的過程中,會將
小數部份無條件去掉,而得到整數部份,這個整數部份的值會被儲到一個型態為int的臨時物件
,型別轉換中因為型態不同而會產生的臨時物件(temporary object),當你的整數部份大於
int型態所能接受的範圍時,此時會產生數值溢位,所以臨時物件所含的值就已不是當初所計
算的值了。那為什麼會變成負呢?數值是循環的,什麼意思呢?像這樣:
...→MIN_VALUE →MAX_VALUE→...→...→1→0→-1→...→MIN_VALUE→MAX_VALUE→...
不相信的話,看個簡單範例。
範例2:
::::::::::::::::::::::::::::::::::::::::::::::
void main()
{
int num = 2147483647;
cout<<num+1<<endl //-2147483648
<<num+2<<endl //-2147483647
<<num+3<<endl //-2147483646
<<num+4<<endl; //-2147483645
num = -2147483648;
cout<<num-1<<endl //2147483647
<<num-2<<endl //2147483646
<<num-3<<endl //2147483645
<<num-4<<endl; //2147483644
}
::::::::::::::::::::::::::::::::::::::::::::::
數值是循環的,最大值再加一時,就跑到最小值;最小值再加1時,就跑到最大值。
所以範例1這個程式還是不夠嚴謹,那怎麼修正呢?要怎麼利用浮點數將整數部份留下
來而將小數部份拿掉呢?有一個函數能幫你解決這個問題:
floor取最大整數: double floor(double x);
現在來修改這個程式:
範例3:
::::::::::::::::::::::::::::::::::::::::::::::
#include<iostream>
#include<cmath> //for floor()
using namespace std;
double round(double num, unsigned precision)
{
if(num > 0)
return floor(num * pow(10.0, precision) + .5) / pow(10.0, precision);
return floor(num * pow(10.0, precision) - .5) / pow(10.0, precision);
}
void main()
{
cout<<fixed<<round(21474836490.567,2)<<endl;
cout<<fixed<<round(-0.5696,2)<<endl; //這個有問題
}
::::::::::::::::::::::::::::::::::::::::::::::
雖然改善了使用範圍的限制,但誤差變大了使得四捨五入也不準了!!!
浮點數本身就有誤差,再經過加減乘除後誤差就更大了,使得四捨五入已無法
正確的判斷!!!
浮點值愈大或愈小誤差就愈大,愈不容易得到正確的數值結果,誤差是浮動的
只要數值不是太大或太小,大致上結果不會差太多!!!
誤差怎麼來的呢?在PC內小點數的表示是近似而不是精確的,像0.2怎麼表示
成二進位呢?0.22、0.222、0.2222怎麼表示呢?只能採取近似的方法儲存到記憶體中。
Written By James On 2004/02/08