您的位置:首页 > 编程语言 > C语言/C++

C语言实现输出单精度、双精度小数十进制形式

2013-12-07 23:38 337 查看
在C语言里,用printf即可格式化输出小数,但是在某些特殊的场合,这些库函数却是不能使用的。故特意查找了一下单精度、双精度小数的二进制编码,自己实现一个输出十进制字符串的函数。具体代码如下所示。

#include"decimalString.h"
#include<stdio.h>

//若judge为0则返回ret值
#define RETURN_IF_ZERO(judge,ret) do{if(!(judge))return (ret);}while(0)
#define MARK(len) ((1<<(len))-1)//二进制长度len的掩码
#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<<e)|HIGH_BINARY(w,e);
    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));
}

结果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: