鱼C论坛

 找回密码
 立即注册
查看: 656|回复: 2

[已解决]关于打印格式的问题

[复制链接]
发表于 2020-4-1 23:32:37 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
如果用错误的格式打印出的数字有什么规律。比如用%d打印3.1416会出现776530087的数字,那么这个数字又是如何产生的呢??
最佳答案
2020-4-2 12:58:51
计算机中浮点数是以IEEE754的新格式存放
一个32位的浮点数,其最高位是符号位,然后有8位是阶码,最后有23位尾数
符号位不必说,0表示正,1表示负
浮点数的存放,先将浮点数转为1.n*2^m这种形式,其中m为阶码数,n为尾数
下面我们看一下3.1416的存储
最高位符号位为0
3.141600=1.5708*2^1
阶码数是1,尾数是0.5708
阶码数转为阶码是1+127=128  二进制为1000 0000
尾数0.5708转成23位尾数是  100 1001 0000 1111 1111 1001
尾数转二进制的方法很简单,不断的乘2,,每乘一次2 ,就写一位,乘的结果没超过1就写0,超过1,就写1并减去1,一直乘到够自己用,或者没有小数为止

这里我们就得到了3.1416存储的这32位数 0100 0000 0100 1001 0000 1111 1111 1001
十进制就是   1078530041   ,十六进制就是0X40490FF9

那么,问题来了。那个776530087是怎么来的?它是个什么鬼?
这个问题是C语言的存储与printf输出之间的差异造成的。
printf格式化输出,%f既可以输出单精度(32位)也可以输出双精度(64位),所以不管是单精度还是双精度,在输出之前都会被转成双进度。
3.141600转成双进度存储格式为01000000 00001001 00100001 11111111 00101110 01001000 11101000 10100111。 1位符号位,11位阶码,52位尾数(可以看一下,这个52位尾数的前23位和上面的那个23为尾数是一样的的)
然后以%d输出实际上输出的是它的前32位,注意,所谓的前32位不是上面这一串的前32位,计算机中小端存储,低位在前,高位在后,所以输出的实际上是那64位的后半部分。

可以用union看一下他们的存储。
  1. #include <stdio.h>
  2. union Data{
  3.         float a;
  4.         short b[2];
  5.         char c[4];
  6.         int d;
  7. };
  8. union Num{
  9.     double lf;
  10.     int d[2];
  11.     char ch[8];
  12. };
  13. int main()
  14. {
  15.     union Data aaa;
  16.     aaa.a=3.141600;
  17.     int i=0;
  18.     printf("float类型的3.141600存储,占4个字节\n");
  19.     printf("a=%f\n两个双字节以16进制显示:",aaa.a);
  20.     for(i=0;i<2;i++)
  21.         printf("b[%d]=%x  ",i,aaa.b[i]);
  22.     printf("\n两个双字节以10进制显示:");
  23.     for(i=0;i<2;i++)
  24.         printf("b[%d]=%d\t",i,aaa.b[i]);
  25.     printf("\n四个单字节:");
  26.     for(i=0;i<4;i++)
  27.         printf("c[%d]=%d\t",i,aaa.c[i]);
  28.         printf("\n一个四字节以16进制显示:d=%x\n",aaa.d);
  29.     union Num num;
  30.     num.lf=3.141600;
  31.     printf("\ndouble类型的3.141600存储,占8个字节\n");
  32.     printf("f=%f\n两个四字节以16进制显示:",num.lf);
  33.     for(i=0;i<2;i++)
  34.         printf("d[%d]=%x\t",i,num.d[i]);
  35.     printf("\n两个四字节以10进制显示:");
  36.     for(i=0;i<2;i++)
  37.         printf("d[%d]=%d\t",i,num.d[i]);
  38.     printf("\n八个单字节:");
  39.     for(i=0;i<8;i++)
  40.         printf("ch[%d]=%d\t",i,num.ch[i]);
  41.     return 0;
  42. }
复制代码


结果:
  1. float类型的3.141600存储,占4个字节
  2. a=3.141600
  3. 两个双字节以16进制显示:b[0]=ff9  b[1]=4049  
  4. 两个双字节以10进制显示:b[0]=4089        b[1]=16457       
  5. 四个单字节:c[0]=-7        c[1]=15        c[2]=73        c[3]=64       
  6. 一个四字节以16进制显示:d=40490ff9

  7. double类型的3.141600存储,占8个字节
  8. f=3.141600
  9. 两个四字节以16进制显示:d[0]=2e48e8a7        d[1]=400921ff       
  10. 两个四字节以10进制显示:d[0]=776530087        d[1]=1074340351       
  11. 八个单字节:ch[0]=-89        ch[1]=-24        ch[2]=72        ch[3]=46        ch[4]=-1        ch[5]=33        ch[6]=9        ch[7]=64       
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-4-1 23:37:33 | 显示全部楼层
会把浮点数的存储格式作为整数来解释

https://www.cnblogs.com/jillzhang/archive/2007/06/24/793901.html
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-4-2 12:58:51 | 显示全部楼层    本楼为最佳答案   
计算机中浮点数是以IEEE754的新格式存放
一个32位的浮点数,其最高位是符号位,然后有8位是阶码,最后有23位尾数
符号位不必说,0表示正,1表示负
浮点数的存放,先将浮点数转为1.n*2^m这种形式,其中m为阶码数,n为尾数
下面我们看一下3.1416的存储
最高位符号位为0
3.141600=1.5708*2^1
阶码数是1,尾数是0.5708
阶码数转为阶码是1+127=128  二进制为1000 0000
尾数0.5708转成23位尾数是  100 1001 0000 1111 1111 1001
尾数转二进制的方法很简单,不断的乘2,,每乘一次2 ,就写一位,乘的结果没超过1就写0,超过1,就写1并减去1,一直乘到够自己用,或者没有小数为止

这里我们就得到了3.1416存储的这32位数 0100 0000 0100 1001 0000 1111 1111 1001
十进制就是   1078530041   ,十六进制就是0X40490FF9

那么,问题来了。那个776530087是怎么来的?它是个什么鬼?
这个问题是C语言的存储与printf输出之间的差异造成的。
printf格式化输出,%f既可以输出单精度(32位)也可以输出双精度(64位),所以不管是单精度还是双精度,在输出之前都会被转成双进度。
3.141600转成双进度存储格式为01000000 00001001 00100001 11111111 00101110 01001000 11101000 10100111。 1位符号位,11位阶码,52位尾数(可以看一下,这个52位尾数的前23位和上面的那个23为尾数是一样的的)
然后以%d输出实际上输出的是它的前32位,注意,所谓的前32位不是上面这一串的前32位,计算机中小端存储,低位在前,高位在后,所以输出的实际上是那64位的后半部分。

可以用union看一下他们的存储。
  1. #include <stdio.h>
  2. union Data{
  3.         float a;
  4.         short b[2];
  5.         char c[4];
  6.         int d;
  7. };
  8. union Num{
  9.     double lf;
  10.     int d[2];
  11.     char ch[8];
  12. };
  13. int main()
  14. {
  15.     union Data aaa;
  16.     aaa.a=3.141600;
  17.     int i=0;
  18.     printf("float类型的3.141600存储,占4个字节\n");
  19.     printf("a=%f\n两个双字节以16进制显示:",aaa.a);
  20.     for(i=0;i<2;i++)
  21.         printf("b[%d]=%x  ",i,aaa.b[i]);
  22.     printf("\n两个双字节以10进制显示:");
  23.     for(i=0;i<2;i++)
  24.         printf("b[%d]=%d\t",i,aaa.b[i]);
  25.     printf("\n四个单字节:");
  26.     for(i=0;i<4;i++)
  27.         printf("c[%d]=%d\t",i,aaa.c[i]);
  28.         printf("\n一个四字节以16进制显示:d=%x\n",aaa.d);
  29.     union Num num;
  30.     num.lf=3.141600;
  31.     printf("\ndouble类型的3.141600存储,占8个字节\n");
  32.     printf("f=%f\n两个四字节以16进制显示:",num.lf);
  33.     for(i=0;i<2;i++)
  34.         printf("d[%d]=%x\t",i,num.d[i]);
  35.     printf("\n两个四字节以10进制显示:");
  36.     for(i=0;i<2;i++)
  37.         printf("d[%d]=%d\t",i,num.d[i]);
  38.     printf("\n八个单字节:");
  39.     for(i=0;i<8;i++)
  40.         printf("ch[%d]=%d\t",i,num.ch[i]);
  41.     return 0;
  42. }
复制代码


结果:
  1. float类型的3.141600存储,占4个字节
  2. a=3.141600
  3. 两个双字节以16进制显示:b[0]=ff9  b[1]=4049  
  4. 两个双字节以10进制显示:b[0]=4089        b[1]=16457       
  5. 四个单字节:c[0]=-7        c[1]=15        c[2]=73        c[3]=64       
  6. 一个四字节以16进制显示:d=40490ff9

  7. double类型的3.141600存储,占8个字节
  8. f=3.141600
  9. 两个四字节以16进制显示:d[0]=2e48e8a7        d[1]=400921ff       
  10. 两个四字节以10进制显示:d[0]=776530087        d[1]=1074340351       
  11. 八个单字节:ch[0]=-89        ch[1]=-24        ch[2]=72        ch[3]=46        ch[4]=-1        ch[5]=33        ch[6]=9        ch[7]=64       
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-26 21:03

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表