C++中对浮点数的名目化显示
副标题#e#
在很多应用措施规模中,都需要节制小数点后的小数位,可是浮点数对此不能提供直接的支持。奈何对措施中的浮点数据举办"整齐"地名目化呢?在此我们有一个迂回的要领,先把它们转换为字符串,名目化后以文本形式显示出来。
在日常编程中–包罗对话框、干系数据库、金融措施、SMS措施及一切处理惩罚数据文件的措施,需要节制小数点后的小数位的环境很是普遍,本文中将要讲授如何用简朴的要领来节制小数位,别的,还要揭开字符串及数据精度的一点点小奥秘。
问题的引出
如有一个函数,其可接管一个long double参数,并将参数转换为字符串,功效字符串应保存两位小数,譬喻,浮点值123.45678应该生成"123.45"这样的字符串。外貌上看来这是一个意义不大的编程问题,然而,假如然要在实际中派上用场,函数应设计为具有必然弹性,以答允挪用者指定小数位数。别的,函数也应该可以或许处理惩罚各类异常环境,如像123.0或123这样的整数。
在开始之前,先看一下编写"优雅"C++代码时的两句"真言":
"真言"1:无论何时需要名目化一个数值,都应先转换为一个字符串。这样可担保每位数恰好占据一个字符。
"真言"2:在需要转换为字符串时,请利用<sstream>库。
转换函数的接口很是简捷:第一个参数是需被名目化的数值;第二个参数代表小数点后显示的小数位,且应该具有一个默认值;返回值为一个string范例:
string do_fraction(long double value, int decplaces=3);
留意,第二个参数代表的小数位数中包罗了小数点,因此,两位小数需要默认值为3。
精度问题
虽然,第一步是把long double值转换为一个string,利用尺度C++库<sstream>的确是手到擒来。然而,有一件工作必需引起留意,因为某些原因,stringstream工具默认精度为6,而很多措施员错误地把"精度"领略为小数的位数,这是不正确的,精度应指代全部位数。因而,数字1234.56可安详地通过默认精度6来暗示,但12345.67会被截断为12345.6。这样的话,假如你有一个很是大的数,如1234567.8,它的功效会静暗暗地转换为科学记数法:1.23457e+06,这显然不是我们想要的。为制止这样的贫苦,在开始转换之前,应把默认精度设为最大。
为获得long double能暗示的最大位数,可利用<limits>库:
string do_fraction(long double value, int decplaces=3)
{
int prec=numeric_limits<long double>::digits10; // 18
ostringstream out;
out.precision(prec);//包围默认精度
out<<value;
string str= out.str(); //从流中取出字符串 数值此刻存储在str中,期待名目化。
小数点的位置
要举办名目化,首先要确定小数点的位置,假如小数位多于decplaces,do_fraction()会删除多余的。
要定位小数位,可利用string::find(),在STL算法中利用了一个常量来代表"数值未找到",在字符串中,这个常量为string::npos:
char DECIMAL_POINT='.'; // 欧洲用法为','
size_t n=str.find(DECIMAL_POINT);
if ((n!=string::npos)//是否有小数点呢?
{
//查抄小数的位数
}
#p#副标题#e#
假如没有小数点,函数直接返回字符串,不然,函数将继承查抄小数位是否多于decplaces。假如是,小数部门将会被截断:
size_t n=str.find(DECIMAL_POINT);
if ((n!=string::npos)//有小数点吗?
&&(str.size()> n+decplaces)) //后头至少尚有decplaces位吗?
//在小数decplaces位之后写入nul
str[n+decplaces]='\0';
最后一行包围了多余的小数位,它利用了\0常量来截断字符串,要留意,string工具的数据可以包括nul字符;而字符串的实际长度由size()的返回值抉择。因此,你不能假定字符串已被正确地名目化,换句话来说,假如在str中本来为"123.4567",在插入\0常量之后,它酿成了"123.45\07",为把str缩减为"123.45",一般可利用自互换的要领:
str.swap(string(str.c_str()) );//删除nul之后的多余字符
那它的道理是什么呢?函数string::c_str()返回一个const char *代表此字符串工具,而这个值被用作一个姑且string工具的初始化值,接着,姑且工具又被用作str.swap()的参数,swap()会把值"123.45"赋给str。一些老一点的编译器不支持默认模板参数,大概不会让swap()通过编译,假如是这样的话,利用手工互换来取代:
string temp=str.c_str();
str=temp;
代码虽不是很"美妙",但能到达目标就行。以下是do_fraction()的完整代码:
#p#分页标题#e#
string do_fraction(long double value, int decplaces=3)
{
ostringstream out;
int prec=
numeric_limits<long double>::digits10; // 18
out.precision(prec);//包围默认精度
out<<value;
string str= out.str(); //从流中取出字符串
size_t n=str.find(DECIMAL_POINT);
if ((n!=string::npos) //有小数点吗?
&& (str.size()> n+decplaces)) //后头至少尚有decplaces位吗?
{
str[n+decplaces]='\0';//包围第一个多余的数
}
str.swap(string(str.c_str()));//删除nul之后的多余字符
return str;
}
假如不想通过传值返回一个string工具,还可增加一个参数,把str工具以引用通报:
void do_fraction(long double value, string & str, int decplaces=3);
从小我私家的角度来讲,照旧倾向于让编译器做这样的优化,别的,利用传值返回,还可以让你以下面这种方法利用do_fraction():
cout << funct(123456789.69999001) << ‘\t’ << funct(12.011)<<endl;
输出:
123456789.69 12.01