高效严谨的浮点数四舍五入的方法
求一个高效严谨的浮点数四舍五入的方法
如题。自己写了2个,都不是很满意。
参数d是要转换的数,p是保留到小数点后的位数:
第一个:
这方法由于int比double范围小很多,需要超过9位的小数就不适用了。很不严谨。
第二个:
这个个人觉得效率是不是偏低了点,而且p只能是是一位数的,大于10结果就完全不对。
求下一种更好的方法
------解决方案--------------------
------解决方案--------------------
浮点数不精确,所以不存在严谨的做法。
------解决方案--------------------
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
------解决方案--------------------
无论低效高效都会入舍不准!
如题。自己写了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结果就完全不对。
求下一种更好的方法
------解决方案--------------------
------解决方案--------------------
浮点数不精确,所以不存在严谨的做法。
------解决方案--------------------
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
------解决方案--------------------
无论低效高效都会入舍不准!