zoukankan      html  css  js  c++  java
  • c++中double转换成string型(浮点数的格式化)

    在日常编程中--包括对话框、关系数据库、金融程序、SMS程序及一切处理数据文件的程序,需要控制小数点后的小数位的情况非常普遍,本文中将要讲 解如何用简单的方法来控制小数位,另外,还要揭开字符串及数据精度的一点点小秘密。

    问题的引出

    如有一个函数,其可接受一个long double参数,并将参数转换为字符串,结果字符串应保留两位小数,例如,浮点值123.45678应该生成“123.45”这样的字符串。表面上看来 这是一个意义不大的编程问题,然而,如果真要在实际中派上用场,函数应设计为具有一定弹性,以允许调用者指定小数位数。另外,函数也应该能够处理各种异常 情况,如像123.0或123这样的整数。

    在开始之前,先看一下编写“优雅”C++代码时的两句“真言”:

    “真言”1:无论何时需要格式化一个数值,都应先转换为一个字符串。这样可保证每位数刚好占据一个字符。

    “真言”2:在需要转换为字符串时,请使用库。

    转换函数的接口非常简洁:第一个参数是需被格式化的数值;第二个参数代表小数点后显示的小数位,且应该具有一个默认值;返回值为一个string类 型:

     

    string do_fraction(long double value, int decplaces=3);

     

    注意,第二个参数代表的小数位数中包括了小数点,因此,两位小数需要默认值为3。

    精度问题

    当然,第一步是把long double值转换为一个string,使用标准C++库简直是手到擒来。然而,有一件事情必须引起注意,因为某些原 因,stringstream对象默认精度为6,而许多程序员错误地把“精度”理解为小数的位数,这是不正确的,精度应指代全部位数。因而,数字 1234.56可安全地通过默认精度6来表示,但12345.67会被截断为12345.6。这样的话,如果你有一个非常大的数,如1234567.8, 它的结果会静悄悄地转换为科学记数法:1.23457e+06,这显然不是我们想要的。为避免这样的麻烦,在开始转换之前,应把默认精度设为最大。
    为 得到long double能表示的最大位数,可使用库:

     

    string do_fraction(long double value, int decplaces=3) { int prec=numeric_limits::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)//是否有小数点呢? { //检查小数的位数 }

     

    如果没有小数点,函数直接返回字符串,否则,函数将继续检查小数位是否多于decplaces。如果是,小数部分将会被截断:

     

    size_t n=str.find(DECIMAL_POINT); if ((n!=string::npos)//有小数点吗? &&(str.size()> n+decplaces)) //后面至少还有decplaces位吗? //在小数decplaces位之后写入nul str[n+decplaces]='';

     

    最后一行覆盖了多余的小数位,它使用了常量来截断字符串,要注意,string对象的数据可以包含nul字符;而字符串的实际长度由 size()的返回值决定。因此,你不能假定字符串已被正确地格式化,换句话来说,如果在str中原来为“123.4567”,在插入常量之后,它变 成了“123.457”,为把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()的完整代码:

     

    string do_fraction(long double value, int decplaces=3) {  ostringstream out;  int prec=  numeric_limits::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]='';//覆盖第一个多余的数 }  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) << ' ' << funct(12.011)<<endl;

     

    输出:123456789.69 12.01

  • 相关阅读:
    OleDbCommand 的用法
    递归求阶乘
    C#重写窗体的方法
    HDU 5229 ZCC loves strings 博弈
    HDU 5228 ZCC loves straight flush 暴力
    POJ 1330 Nearest Common Ancestors LCA
    HDU 5234 Happy birthday 01背包
    HDU 5233 Gunner II 离散化
    fast-IO
    HDU 5265 pog loves szh II 二分
  • 原文地址:https://www.cnblogs.com/For-her/p/3909321.html
Copyright © 2011-2022 走看看