扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
#include stdio.h
专注于为中小企业提供成都网站建设、网站制作服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业云霄免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了上1000+企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
#include stdlib.h
struct fraction
{
int numerator; // 分子
int denominator; // 分母
int symbol; // 符号,分子为负时取值为-1,为正时取值为1,其它取值无意义
};
int scan_frac(struct fraction *f, struct fraction *g);// 按题目格式输入两个分数,存入f和g所指的内存中。
struct fraction multiply_frac(struct fraction f, struct fraction g); //返回分数f和g的乘积。
int main()
{
struct fraction f1, f2, product;
while(scan_frac(f1, f2) != EOF)
{
product = multiply_frac(f1, f2);
if(product.numerator == 0)
{
printf("0\n");
continue;
}
if(product.symbol == -1)
printf("-");
printf("%d/%d\n", product.numerator, product.denominator);
}
return 0;
}
//读取
int scan_frac(struct fraction *f, struct fraction *g)
{
char c=0;
int *tmpnum=f-numerator;
*tmpnum=0;
f-symbol=1;
while((c=getchar())!='\n')
{
if(c='9'c='0')
*tmpnum=*tmpnum*10+c-'0';
else if(c=='/')
{
tmpnum=f-denominator;
*tmpnum=0;
}
else if(c=='-')
f-symbol=-1;
}
tmpnum=g-numerator;
*tmpnum=0;
g-symbol=1;
while((c=getchar())!='\n')
{
if(c='9'c='0')
*tmpnum=*tmpnum*10+c-'0';
else if(c=='/')
{
tmpnum=g-denominator;
*tmpnum=0;
}
else if(c=='-')
g-symbol=-1;
}
fflush(stdin);
return c;
}
//乘积
struct fraction multiply_frac(struct fraction f, struct fraction g)
{
int i=0;
int tmp1,tmp2;
struct fraction *result=(struct fraction *)malloc(sizeof( struct fraction));
//约分
tmp1=f.denominator*g.denominator;
tmp2=f.numerator*g.numerator;
i=tmp1tmp2?tmp2:tmp1;
while(i=2)
{
if(tmp1%i==0
tmp2%i==0)
{
tmp1/=i;
tmp2/=i;
}
i--;
}
result-denominator=tmp1;
result-numerator=tmp2;
result-symbol=f.numerator*g.numerator;
return *result;
}
#include stdio.h typedef struct node{ long numerator,denominator; void frac_add(struct node,struct node); }fraction; void fraction::frac_add(fraction f1,fraction f2){ long i=2; this-denominator=f1.denominator*f2.denominator; this-numerator=f1.denominator*f2.numerator+f1.numerator*f2.denominator; while(i=this-denominatori=this-numerator){ while(!(this-denominator%i||this-numerator%i)){ this-denominator/=i; this-numerator/=i; } i++; } } void main(){ fraction f1,f2,f3; f1.numerator=3; f1.denominator=5; f2.numerator=4; f2.denominator=35; f3.frac_add(f1,f2); printf("%ld/%ld+%ld/%ld=%ld",f1.numerator,f1.denominator,f2.numerator,f2.denominator,f3.numerator); if(f3.numerator!=1){ printf("/%ld",f3.denominator); }putchar('\n'); }
IEEE浮点表示
IEEE浮点标准用
df5100123a813f9090d121b75ced0031.png
的形式近似表示一个数。并且将浮点数的位表示划分为三个字段:
符号(sign)s决定这个数是负数(s=1)还是正数(s=0)。可以用一个单独的符号s直接编码符号s。
尾数(signficand)M是一个二进制小数,它的范围是1~2-ξ或者是0~1-ξ。
n位小数字段87a21568c1ae9de8730727ed0bd6b560.png编码尾数M。
阶码(exponent)E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)。k位的阶码字段 721b2fea992881e3b5d58079b970ae49.png编码阶码E。
在单精度浮点格式(c语言的float)中,s,exp和frac字段分别为1位,8位和23位,而双精度浮点格式(c语言中的double)中,s,exp和frac字段分别为1位,11位和52位。
一个浮点数的常见比特位表示如下:
单精度
双精度
而根据exp的值,被编码的值可以分为三大类不同的情况。下面进行一一解释。
情况1:规格化的值
即最普遍的情况,当exp,即阶码域既不为全0,也不为全1的情况。在这种情况下,阶码字段解释为以偏置(biased)形式表示有符号整数,即E=exp-Bias,exp是无符号数(1~254)。Bias是一个等于534c19b7245290e31ebf91380d5f76cc.png的偏置值,对于单精度来说,k=23,Bias=127,因此E的范围是-126~+127。
frac被描述为小数值,且0≤frac1,其二进制表示为0.frac。尾数定义为 M=1+frac ,则M=1.frac。那么就有1≤M2,由于总是能够调整阶码E,使得M在范围1≤M2,所以不需要显示的表示它,这样还能获得一个额外的精度位。也就是说,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的frac部分,等到读取的时候,再把第一位的1加上去。
情况2:非规格化的值
当exp,即阶码域为全0时,所表示的数便为非规格化的值,该情况下的阶码值E=1-Bias(注:为从非格式化值转换到格式化值提供了一种方法)。尾数M=frac
非规格化的数有两个作用。
表示数值0。格式化数中,我们总使得M≥1,因此就无法表示0。而阶码全0时,且尾数也全0时,就可以表示0了。
表示接近0.0的数。它所表示的值分布地接近于0.0,该属性成为逐渐溢出。
情况3:特殊值
有两种
阶码全为1,小数域全为0。它得到值为 +∞(s=0)或-∞(s=1),它在计算机中可以表示溢出的结果,例如两个非常大的数相乘。
阶码全为1,小数域不全为0。它得到值为NaN(Note a Number)。它在计算机中可以表示非法的数,例如计算根号-1时的值。
有了前面了基础,我们就可以来计算浮点数的数值范围了。以单精度(float)为例,我们知道它的指数范围(即E)为-126~+127,而M的范围为1≤M2,实际上,对于单精度,1≤M≤2-2^(-23)(注:23为frac字段所占的比特位)。那么我们就可以得到单精度的最大值为:
e20b1b498c9971264f71d21d95b4fd55.png
同理,我们可以得到单精度的最小值为:
473794d6f5217c3819363802d1afd92c.png
我们仅仅以单精度为例,用同样的方法可以计算其他精度的浮点数数值范围,在此不再赘述。
浮点数的有效位
有效位也可以理解为我们常说的精度。浮点数的精度是由尾数的位数来决定的。
对于单精度(float),它的尾数为23位,而2^23=8388608,共7位,也就是说最多能有7位有效数字,但至少能保证6位,因此其有效位为6~7位。当然我们可以通过下面的内容进一步理解。以下计算结果保留10位小数。
2946a6b34bb505a6dbcb919b2135f385.png5b3381a272e552309bd42b8f41d28d21.pnge10632444e7356476404af36db2c1bff.png
观察a和b的结果可以发现,0.0000001和0.0000002之间的其他数是没有办法通过单精度浮点数来精确表示的,也就是说,只有到小数点后面7位的值才是精确的,同理,观察b和c的结果,0.0000002到0.0000004之间的其他数也是不能通过单精度浮点数精确表示的,更不幸地是,这之间的数,甚至只能精确到第6位。
这也就有了单精度浮点数的有效位为6~7位的结论。根据相似的方法,我们同样可以得到双精度浮点数的有效位为15~16位的结论,这里不再赘述。
浮点数在内存中的存储
了解了这么多,我们来看一下一个小数究竟是如何在内存中存储的。以float f = 8.5f为例。其二进制表示为94e38714fa97873bae95878e37c505e7.png,可见指数实际值为3,则根据E=exp-Bias,可知exp=E+Bias=3+127=130,根据M=1+frac,可知,frac=M-1=0.0001(二进制)而45db4687f18f997dac6756f04478fe85.png
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流