在C语言里,用printf即可格式化输出小数,但是在某些特殊的场合,这些库函数却是不能使用的。故特意查找了一下单精度、双精度小数的二进制编码,自己实现一个输出十进制字符串的函数。具体代码如下所示。
#include"decimalString.h"
#include
//若judge为0则返回ret值
#define RETURN_IF_ZERO(judge,ret) do{if(!(judge))return (ret);}while(0)
#define MARK(len) ((1<
#define LOW_BINARY(d,low) ((d)&MARK(low))//取双字d的低low位
#define HIGH_BINARY(d,high) (((d)>>(32-(high)))&MARK(high))//取双字d的高high位int lengthOfInt(int a);//a十进制绝对值的位数
//整数a十进制写入到字符串buf中
char*intToStr(int a,char*buf,int*length);
//将小数点后若干高位写入字符串,高位为0需补上
//0<=decimal<1,且写入位数writeLen<=7
char*decimalToStr(double decimal,int writeLen,char*buf);
int floatToInt(float f){//单精度的整数部分
return doubleToInt(f);
}
//双精度小数取整
//适用范围:数值在int整型范围以内
int doubleToInt(double db){
const int eOffset=1023;//指数偏移
const int wWidth=52,eWidth=11;//尾数位数,指数位数
int *binary=(int*)&db;
int sign,e,w,r;//e:指数,w:尾数
sign=binary[1]>=0?1:-1;//正负号
e=LOW_BINARY(HIGH_BINARY(binary[1],1+eWidth),eWidth);
e-=eOffset;
RETURN_IF_ZERO(e>=0,0);
RETURN_IF_ZERO(e!=0,sign);
RETURN_IF_ZERO(e<=30,0x7ffffff*sign);
w=LOW_BINARY(binary[1],wWidth-32);
w<<=(64-wWidth);
w|=HIGH_BINARY(binary[0],64-wWidth);
r=(1<
return sign*r;
}
//双精度小数f转换成小数位数precision位(最多13位小数)的十进制字符串
//小数范围需要在int范围以内
char*doubleToStr(double f,int precision,char*buf){
const int limit=7,dLimitTime=10000000;//小数位数一次写入位数最大限制
int len,zPart,dHign,sign;
char*tmp=buf;
double js=0.5;//四舍五入近似
sign=f<0?-1:1;//正负号
if(sign==-1)
*tmp++='-';
f*=sign;//取绝对值
precision=precision<0?0:precision;
precision=precision>13?13:precision;
len=precision;
while(len-->0)
js/=10;
f+=js;//四舍五入
zPart=doubleToInt(f);//整数部分
intToStr(zPart,tmp,&len);//写入整数部分
if(precision>0){//小数位数要求>0
dHign=precision<=limit?precision:limit;
tmp[len]='.';
tmp+=len+1;
f=f-zPart;//小数部分
decimalToStr(f,dHign,tmp);//小数高位部分先写入字符串
if(precision>limit){//limit位之后的小数的需要另外写入
f=f*dLimitTime;//放大倍数
f-=doubleToInt(f);//
decimalToStr(f,precision-limit,tmp+limit);
}
}
return buf;
}
//单精度小数f转换成小数位数precision位(最多7位小数)的十进制字符串
//小数范围需要在int范围以内
char*floatToStr(float f,int precision,char*buf){
precision=precision>7?7:precision;
return doubleToStr(f,precision,buf);
}
//整数a十进制写入到字符串buf中
char*intToStr(int a,char*buf,int*length){
int pos=0;
char*tmp;
if(a<0){
a*=-1;
buf[pos++]='-';
}
tmp=buf+pos+lengthOfInt(a);
if(length!=NULL)*length=tmp-buf;
*tmp='\0';
while(a>9){
*--tmp=0x30|(a%10);
a/=10;
}
*--tmp=0x30|a;//0x30='0'
return buf;
}
//将小数点后若干高位写入字符串,高位为0需补上
//0<=decimal<1,且写入位数writeLen<=7
char*decimalToStr(double decimal,int writeLen,char*buf){
int t,tmp=writeLen;
t=1;
while(tmp-->0)
t*=10;
tmp=doubleToInt(decimal*t);//乘上10^writeLen再取整
buf[writeLen]='\0';
for(t=--writeLen;t>=0;t--){
buf[t]=(tmp%10)|0x30;
tmp/=10;
}
return buf;
}
int lengthOfInt(int a){//a十进制绝对值的位数
int len=1;
a=a>0?a:-a;
while(a>9){
len++;
a/=10;
}
return len;
}
测试函数如下:
void testFDouble(){
double d2=12;
float f2=-100,f3=12.8,f4=0.09;
char tmp[65];
for(int k=0;k<40;k++){
double f=f2+k*10.0704008979423463423;
//printf("%f-强制转换整数:%d\n",f,doubleToInt(f));
printf("%+.12f,函数输出:%s\n",f,doubleToStr(f,12,tmp));
}
//printf("%f-强制转换整数:%d\n",-0.1,doubleToInt(-0.1));
//printf("%f-强制转换整数:%d\n",0.1,doubleToInt(0.1));
//printf("%f-强制转换整型:%d;函数转换:%d\n",f3,(int)f3,floatToInt(f3));
//printf("%f-强制转换整型:%d;函数转换:%d\n",f4,(int)f4,floatToInt(f4));
//printf("%s\n",intToBinary(12,tmp));
//printf("%s\n",intToBinary(*(int*)&f2,tmp));
//printf("%s\n",doubleToBinary(d2,tmp));
}
结果如下: