高效严谨的浮点数四舍五入的方法

求一个高效严谨的浮点数四舍五入的方法
如题。自己写了2个,都不是很满意。
参数d是要转换的数,p是保留到小数点后的位数:

第一个:
C/C++ code

double round(double d,unsigned p)
{
    if(d==0.0)
        return 0.0;
    double n=10;
    for(;p>0;p--)
        n*=10;
    if(d>0.0)
        return (long)(d*n+5) / 10 / (n/10);
    else
        return (long)(d*n-5) / 10 / (n/10);
}


这方法由于int比double范围小很多,需要超过9位的小数就不适用了。很不严谨。

第二个:
C/C++ code

double round(double d,unsigned p)
{
    //先转成字符串
    char s[20];
    sprintf(s,"%.*f",p,d);
    //再转成double
    char*sp=s;
    bool neg_flag=false;
    double res=0;
    int x=0,y=1;
    if(sp[0] == '+' || sp[0] == '-')
        neg_flag = (*sp++ != '+');
    while(isdigit(*sp))
        res = res * 10 + (*sp++ - '0');
    if(*sp++=='.')
    {
        while(isdigit(*sp))
        {
            x = x * 10 + (*sp++ - '0');
            y*=10;
        }
        res+=x*1.0/y;
    }
    return neg_flag ? -res : res;
}


这个个人觉得效率是不是偏低了点,而且p只能是是一位数的,大于10结果就完全不对。

求下一种更好的方法


------解决方案--------------------
探讨
double round(double d,unsigned p)
{
char chs[10],data[200]
sprintf(chs,"%%.%uf",p);
sprintf(data,chs,d);
return atof(data);
}
我用TC2.0编译发现sprintf自己四舍五入了,怀疑不同编译器情况不一样.
如果sprintf不会四舍五入的话,加上下面一段;
……

------解决方案--------------------
浮点数不精确,所以不存在严谨的做法。
------解决方案--------------------
http://wenku.baidu.com/view/7c7e79f7ba0d4a7302763a94.html

//小数点后1位4舍5入,没处理负数。

float round(float f){
__int32 *p=(__int32*)&f;

//是否负数,负数的舍入逻辑是否和正数一样?
bool sign=false;

if(*p&0x80000000)
sign=true;
__int32 exp=(*p& (~0x80000000))>>23;
exp-=127;

if(exp>=23)//没有小数部分
return f;

__int32 num=(1<<23)|(*p&0x7fffff);

//num为定点数。
//1.xxxx * 2^exp
if(exp>=0){
//小数点后1位位置 1<<(22-exp)
int shift=22-exp;
if(num&(1<<shift)){
num+=(1<<shift);
num&=-1<<shift;
if(num&(1<<24)){
num>>=1;//定点数部分/2
++exp;//exp+1
}
}else{
//截断小数
num&=~(1<<shift);
}
exp+=127;
*p=(exp<<23)|(num&0x7fffff);
return f;
}else{
int shift=22-exp;
if(shift>23){//小于0.5
return 0.0f;
}
++exp;
exp+=127;
*p=(exp<<23);
return f;
}
}



------解决方案--------------------
实现么……翻标准库实现去吧。不过搞不好是硬件直接实现的(否则C标准库搞这么一大堆接口就没太大意义了……)。
<cmath>

std::round
std::lround
std::llround
std::rint
std::lrint
std::llrint

<cfenv>
std::fegetround
std::fesetround

------解决方案--------------------
无论低效高效都会入舍不准!