C语言中printf用%d输出float类型数据,或以%f输出int型数据的结果

1.测试程序及结果

  • 程序
#include"stdio.h"
int main()
{
	float a = 7.5, b = 1.23, c = 1.24, d = 1.25;
	double a1 = 7.5, b1 = 1.23, c1 = 1.24, d1 = 1.25;
	int e = 0, f = 1,g= 0x7fffffff;
	unsigned  int h = 0xffffffff;
	unsigned long long i = 0x401E000000000000,j= 0x3FF3AE147AE147AE,k= 0x3FF3D70A3D70A3D7,l= 0x3FF4000000000000;
	printf("7.5_  d:%d\n7.5_x:0x%x\n7.5_llx:0x%llx\n", a,a,a);
	printf("7.5_  d:%d\n7.5_x:0x%x\n7.5_llx:0x%llx\n\n",a1,a1 ,a1);
	printf("1.23_  d:%d\n1.23_x:0x%x\n1.23_llx:0x%llx\n", b,b,b);		
	printf("1.23_  d:%d\n1.23_x:0x%x\n1.23_llx:0x%llx\n\n", b1,b1,b1);	
	printf("1.24_  d:%d\n1.24_x:0x%x\n1.24_llx:0x%llx\n", c,c,c);	
	printf("1.24_  d:%d\n1.24_x:0x%x\n1.24_llx:0x%llx\n\n",c1,c1, c1);	
	printf("1.25_  d:%d\n1.25_x:0x%x\n1.25_llx:0x%llx\n",d,d, d);
	printf("1.25_  d:%d\n,1.25_x:0x%x\n1.25_llx:0x%llx\n\n",d1,d1, d1);
	
	printf("0_f:%f\n\n",e);
	printf("1_f:%f\n\n", f);
	printf("0x7fffffff_f:%f\n\n",g);
	printf("0xffffffff_f:%f\n\n", h);
	printf("0x401E000000000000_f:%f\n\n", i);
	printf("0x3FF3AE147AE147AE_f:%f\n\n",j);
	printf("0x3FF3D70A3D70A3D7_f:%f\n\n", k);
	printf("0x3FF4000000000000_f:%f\n\n",l);
	return 0;
}

  • 结果:
C语言中printf用%d输出float类型数据,或以%f输出int型数据的结果_第1张图片
图1

2.结果分析

  • IEEE754标准
(1) 单精度(32位)浮点数的结构:
C语言中printf用%d输出float类型数据,或以%f输出int型数据的结果_第2张图片
图2

名称                                         长度        比特位置

符号位    Sign  (S)             : 1bit       (b31)
指数部分Exponent (E)      : 8bit       (b30-b23)
尾数部分Mantissa   (M)    : 23bit     (b22-b0)

其中的指数部分(E)采用的偏置码(biased)的形式来表示正负指数,若E<127则为负的指数,否则为非负的指数。

另外尾数部分M存储的是当把一个浮点数规范化表示后的1.zozooz...(二进制的)形式的zozooz的部分的比特串,共23位.

求值方法: (-1)^S*(1.M)*2^(E-127)  (公式1)

注意:%f输出float类型,输出6位小数,有效位数一般为7位;

(2) 双精度(64位)浮点数的结构:
C语言中printf用%d输出float类型数据,或以%f输出int型数据的结果_第3张图片
图3

名称                                        长度        比特位置

符号位    Sign  (S)            : 1bit        (b63)
指数部分Exponent (E)     : 11bit      (b62-b52)
尾数部分Mantissa   (M)   : 52bit      (b51-b0)

双精度的指数部分(E)采用的偏置码为1023

求值方法:(-1)^S*(1.M)*2^(E-1023)   (公式2)

注意:双精度数也可用%f格式输出,它的有效位一般为16位,给出小数6位。(这一点在计算金额的时候尤为重要,超过有效位的数字是无意义的,一般会出错。)

  • 具体分析过程

1. float  a=7.5, doule a1=7.5 结果解释:

(7.5)10=(111.1)2=1.111*2^2;

以Float在内存中存储:

S=0;

E=(2+127)10=10000001;

M=111;

C语言中printf用%d输出float类型数据,或以%f输出int型数据的结果_第4张图片

图4

以Double在内存中存储:

S=0;

E=(2+1023)10=( 10000000001)2

M=111;

C语言中printf用%d输出float类型数据,或以%f输出int型数据的结果_第5张图片

图5

虽然7.5在内存中以float(32Bit)存储,但是在printf函数输出的时候要转换为double(64Bit)位的结构(原因:因为float和double类型的数据用printf函数输出的时候都是以%f,没有区别,所以编译器在输出的时候,无论是double还是float类型都已double存储形式输出),所以在程序运行结果中无论是以float类型还是double类型存储的7.5用%llx输出出来都是0x401E000000000000,但是用%d和%x输出的都是0,这是因为%d和%x只取内存的低32位,从图5可以看出低32全为0,所以取出来为0.

2.float b=1.23,double b1= 1.23  结果解释:

(1.23)10=( 1.0011101011100001010001111010111)2= 1.0011101011100001010001111010111*2^0

以float类型在内存中存储:

S=0;

E=(0+127)10=01111111

M=0011101 0111000010100100

C语言中printf用%d输出float类型数据,或以%f输出int型数据的结果_第6张图片

图6


以double类型在内存中存储:

S=0;

E=(0+1023)10=(01111111111)2

M=0011 10101110 00010100 01111010 11100001 01000111 10101110

C语言中printf用%d输出float类型数据,或以%f输出int型数据的结果_第7张图片

图7

可以发现图1中将1.23定义为float类型和double类型打印出来的结果不一样,为什么会有这一现象的发生呢?有两个原因,第一:千万不要以为凡是计算机输出的数字都是精确的。必须知道有些浮点数是没法用二进制精确表示的,例如这里的1.23,在内存中是近似保存的;第二:回到前面解释过的一个问题,float类型在内存中是以(32BIt)存储,首先将1.23以float的形式存储在内存中,存进去的是一个近似的数字,如图8所示,近似为1.230000019073486328125,16进制表示为:0x3f9d70a4,当用printf函数打印的时候,将1.230000019073486328125(而不是1.23)转换为double类型,然后输出,所以结果为图9所示;

C语言中printf用%d输出float类型数据,或以%f输出int型数据的结果_第8张图片

图8

C语言中printf用%d输出float类型数据,或以%f输出int型数据的结果_第9张图片

图9
图9和图1的输出结果统一起来,图1中以float类型存储的1.23,与以double类型存储的1.23结果不同,就是以上的解释;还有一点,程序中用%d打印1.23出来遗传很长的数字,
从图9可以看出,因为内存的低32为不为0,所以才会有很长一串数字,这是因为1.23没法精确表示,只能近似表示导致位数过多。

3.   1.24类似1.23   1.25类似7.5  这里不再解释

4.  整形数字以 %f  输出  的结果说明

unsigned  int h = 0xffffffff,运行结果解释;

这个可以将上面的思路反过来理解,%f每次打印需要获取内存的64bit 并且用IEEE754 双精度浮点数的编码规则解码,
unsigned  int h = 0xffffffff 在内存存储形式如图10,可以看出他的指数位为0,用公式2计算这是一个非常小的数。所以显示出来是0;
C语言中printf用%d输出float类型数据,或以%f输出int型数据的结果_第10张图片
图10

unsigned long long i = 0x401E000000000000 运行结果解释:

这个值在内存中的存储形式就是图5的形式,所以用%f打印出来就是7.5.

3.总结

(1)一般能被精确表示的浮点数(单精度或者双精度),用printf(“%d”)打印的结果一般为0;
(2)不能被精确表示的付点是(单精度或者双精度),用printf(“%d”)打印的结果是一长串数,一般是近似尾数。
(3)整形类型的数,用printf(“%f”)打印出来的结果一般为0;
(4)long long 类型的很大的数,用printf(“%f”)打印出来可能为一个小数;
(5)注意,folat输出6位小数,7位有效位;double 输出6位小数,有效位数为16位,超出有效位,运算时候会出错。

附几个转码工具链接:

进制转换工具:

http://tool.oschina.net/hexconvert/

单精度浮点数内存中编码:

https://www.h-schmidt.net/FloatConverter/IEEE754.html

双精度浮点数内存中编码:

http://www.binaryconvert.com/convert_double.html


你可能感兴趣的:(C语言)