浮點數顯示問題

這篇文章主要是來介紹有關manipulator(操控器)對於浮點數顯示的影響,
當使用到的函數或旗標時,會多加介紹。看個例子:
int main()
{
	double dNum1 = 1.23456789 , dNum2 = 0.123456789 , dNum3 = 0.0123456789 ;

	cout<<"Num1:"<<dNum1<<endl;
	cout<<"Num2:"<<dNum2<<endl;
	cout<<"Num3:"<<dNum3<<endl;

	return 0;
}
輸出為:
Num1:1.23457
Num2:0.123457
Num3:0.0123457
曉得原因嗎?在沒有設定任何的旗標或參數時,輸出的精度(precision)6個十進位數
這個精度的意思是說,小數點前的數字加上小數點後的數字,總共為6個
在顯示數值之前,未滿足精度的位數會做四捨五入(round)的動作,
並且前導的0(leading zero)並不算在精度的位數中。
dNum1顯示六個數字,而最後一個數字是因為後半段四捨五入的關係
dNum2顯示七個數字,因為前導的零(leading)並不算在精度顯示的限制
dNum3顯示八個數字,跟上面的解釋是一樣的
那加點東西來看看:
int main()
{
	double dNum1 = 1.23456789 , dNum2 = 0.123456789 , dNum3 = 0.0123456789 ;

	cout.setf(ios::fixed,ios::floatfield);
	cout<<"Num1:"<<dNum1<<endl;
	cout<<"Num2:"<<dNum2<<endl;
	cout<<"Num3:"<<dNum3<<endl;

	return 0;
}
輸出為:
Num1:1.234568
Num2:0.123457
Num3:0.012346
變了!這是因為設定浮點數表示的方式所造成的!
ios::floatfield是一個遮罩(MASK)
而其中的旗標有
fixed:使用小數記號法
scientific:使用科學記號法 
none:使用上述兩者中最適合者(預設狀況)
setf(ios::fixed,ios::floatfield);這種用法是這樣解釋的:
設置fixed旗標,同時自動清除同組的其他旗標(scientificnone)
所以現在顯示的旗標是設定fixed,
而fixed精度(precision)的定義是指小數點後的數字總數,沒有包含小數點之前的
預設精度(precision)一樣是6,其餘的數字一樣會做四捨五入的動作。
另一種在設置旗標的方式,是直接加在cout<<之後:
cout.setf(ios::fixed,ios::floatfield);
cout<<"Num1:"<<dNum1<<endl;
==>cout<<fixed<<"Num1:"<<dNum1<<endl;
以此類推。
接著接做點改變:
int main()
{
	double dNum1 = 1.23456789 , dNum2 = 0.123456789 , dNum3 = 0.0123456789 ;
	
	cout<<scientific;
	cout<<"Num1:"<<dNum1<<endl;
	cout<<"Num2:"<<dNum2<<endl;
	cout<<"Num3:"<<dNum3<<endl;

	return 0;
}
輸出結果:
Num1:1.234568e+000
Num2:1.234568e-001
Num3:1.234568e-002
顯示 的方式是以科學記號法,e+XXX表示10XXX次方
精度一樣是跟fixed一樣,是指小數點之後的位數,預設為6
而要如何設定精度(precision)呢?
可以利用cout.precision(n);
或直接寫在cout<<之後std::setprecision(n);
但注意利用setprecision需引入<iomanip>標題檔
在網路上查尋到一個範例,讓大家看一下:
#include <iostream>

int main()
{

        double x = 123.456789012345,y = 1.123456789012345e-7;

// No floatfield flags set(沒有設置任何的旗標)

        std::cout << "x " << x << ", y = " << y << '\n';	
      //輸出 x 123.457, y = 1.12346e-007

// Using fixed floating point notation(使用科學記號表示法)

        std::cout.setf(std::ios::fixed,std::ios::floatfield);
        std::cout << "x " << x << ", y = " << y << '\n';
      //輸出 x 123.456789, y = 0.000000

        std::cout.precision(9);
        std::cout << "x " << x << ", y = " << y << '\n';
      //輸出 x 123.456789012, y = 0.000000112


// Restore it to "normal" representation(回復到原本的none或稱為normal旗標)
//但注意精度不是原本的6了,因為上式已經設定精度為9!
//利用fmtflags(0)clear
        std::cout.setf(std::ios::fmtflags(0),std::ios::floatfield);
        std::cout << "x " << x << ", y = " << y << '\n';
      //輸出 x 123.456789, y = 1.12345679e-007

// Revert to fixed(返回到fixed看看)

        std::cout.setf(std::ios::fixed,std::ios::floatfield);
        std::cout << "x " << x << ", y = " << y << '\n';
      //輸出 x 123.456789012, y = 0.000000112

// Try scientific(試看看scientific)

        std::cout.setf(std::ios::scientific,std::ios::floatfield);
        std::cout << "x " << x << ", y = " << y << '\n';
      //輸出 x 1.234567890e+002, y = 1.123456789e-007
}
那先在有一個常見的問題,如何把浮點數轉成字串呢?
在C語言中,可以利用fcvt函數,但由於這個函數並不是標準的C函數,
所以使用編譯器可能會顯示沒有此函數,這個函數的相關資料連結在此:
Convert floating point value to string.char *  fcvt ( double value, int num, int * dec, int * sign );

而在C++也有另一種方法,來看一下程式:
	#include <sstream>
	#include <string>
	#include <iostream>
	#include <iomanip>
	using namespace std;
	int main()
	{
		double fNum = -123.456789;
		string str;
		stringstream sstream;
		sstream<<setprecision(9) <<fNum;
		str = sstream.str();
		cout<<str<<endl;
		return 0;
	}
setprecision(9)中的9是因為在沒有設定任何fixedscientific時,使用的旗標是none
所以精度(precision)的數值代表,小數點前的數字數目加上小數點後的數字數目
其實stringstringstream可以需要自己去研讀了!

網路上搜集來的範例與文章:
Example 6:  Input and Output Expressions
The Standard Library Streams



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

Hosted by www.Geocities.ws

1