四捨五入(round)
四捨五入相信網友一定在求學過程中接觸過,但是否曾經在程式中使用到呢?
記得以前接觸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
你發現到規則了嗎?那寫個程式來看看囉!!!

範例1
::::::::::::::::::::::::::::::::::::::::::::::
#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;
}
::::::::::::::::::::::::::::::::::::::::::::::
[說明]
函式有兩個參數numprecisionprecision(精度)意指要四捨五入到小數點第幾位,
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.220.2220.2222怎麼表示呢?只能採取近似的方法儲存到記憶體中。

回目錄
Written By James On 2004/02/08 

Hosted by www.Geocities.ws

1