浮点数在计算机中的表示,程序中浮点数的取值和比较。

小数的十进制、二进制转换

十进制 --> 二进制

整数部分除2取余,小数部分乘2取整。
考虑 8.25

整数部分8进行除2取余,
除2商4余0
除2商2余0
除2商1余0
除2商0余1
所以结果是1000【最后一个余数在最前】

小数部分0.25进行乘2取整,
0.25乘2得0.5 整数为0
0.5 乘2得1   整数为1
所以结果是01

十进制表示的8.25其二进制表示即为1000.01
 
 

二进制 --> 十进制

这个就简单多了,二进制的各个位乘以其权重即可。

整数部分 1*2^3 + 0*2^2 + 0*2^1 + 0*2^0  = 8
小数部分  0*2^(-1)  +  1*2^(-2)  =  0.25

 
 

小数不能被精确表示的原因

十进制中能够精确表示的小数,都是10的质因子2或5为分母的,比如

1/2=0.5     1/4=0.25      1/8=0.125   
1/5=0.2
1/10=0.1

十进制中不能精确表示的小数,有1/3,1/6,1/7,1/9

在二进制中,只有2是质因子,对于1/2,1/4,1/8可以精确表示;
但对于1/10=0.1和1/5=0.2都不能精确表示。【1/3,1/6,1/7,1/9也不能】
比如对于十进制表示的0.1取它的二进制表示,则是

小数部分0.1进行乘2取整,
0.1乘2得0.2 整数为0
0.2乘2得0.4 整数为0
0.4乘2得0.8 整数为0
0.8乘2得1.6 整数为1
0.6乘2得1.2 整数为1
0.2乘2得0.4 整数为0
0.4乘2得0.8 整数为0
0.8乘2得1.6 整数为1
发现已经开始循环了!根本无法取到精确值。

出处:https://0.30000000000000004.com/
 
 

计算机中小数的存储

需要先将小数的表示方法进行规格化,即整数部分为1,其余全部是小数部分的科学计数法。
比如

1000.01需要先表示为 1.00001*2^3

之后祭出IEEE 754标准
IEEE 754

符号位:0 表示正,1 表示负
指数位:指数是负数时,小数点需要左移;指数是正数时,小数点需要右移。
1.00001 * 2^3,里面的幂数+3代表小数点需要右移3位。
指数位的长度越长,数值的表达范围越大;
尾数位:小数部分,
1.00001 * 2^3,尾数部分就是 00001,
尾数的长度决定了这个数的精度,因此如果要表示精度更高的小数,则就要提高尾数位的长度;

double 的尾数部分是 52 位,float 的尾数部分是 23 位,但他们同时都隐含一个整数部分的1,
所以 double 共有 53 个二进制有效位,float 共有 24 个二进制有效位,
所以它们的精度在十进制中分别是 log10(2^53)约等于 15.95 和 log10(2^24)约等于 7.22 位,
因此 double 的有效数字是 15~16 位,float 的有效数字是 7~8 位,有效位包含整数部分和小数部分;

指数位里面的偏移量

在float类型当中
小数点移动范围是 -126~127
偏移量是127(0111 1111)
这样可以保证8位的指数位都是正数。

1000.01 规范化为 1.00001 * 2^3 
这里的指数位是 +3+127
0.00101 规范化为 1.01 * 2^(-3)
这里的指数位是 -3+127


在double类型当中
小数点移动范围是 -1022~1023
偏移量是1023(0011 1111 1111)
这样可以保证11位的指数位都是正数。

我们来填个空
1.00001 * 2^3
float类型在计算机中表示为
符号01位 0
指数08位 3+127 = 130 = 1000 0010
尾数23位 0000 1000 0000 0000 000
连起来
0 100 0001 0 000 0100 0000 0000 0000 0000
小端存储,低位在低地址,即为 0x41040000

这里有个网站,可以在线查看。
http://www.binaryconvert.com/result_float.html?decimal=056046050053
在这里插入图片描述
 
 

浮点数在计算机中的精度

double 的有效数字是 15~16 位,float 的有效数字是 7~8 位,有效位包含整数部分和小数部分;
如果整数部分越长,就会直接导致小数部分减少。精度变低!
如果小数部分最前边又添了几个0,就会直接导致尾数变长,变得更精确。

说明浮点表示仅仅是一种近似的表示方法,不能精确的表示数值,所以有时候大家在编程的过程中明明向float类型变量赋值了一个准确的数据,仿真一看数据成了一个近似值。

因此浮点数作比较不能使用==直接进行比较!
而是使用下面的宏

/***************************************
*Author:(公众号:最后一个bug)
****************************************/
#define FLOAT_EPS (0.000001) //根据需求
#define Float_Equ(a, b) ((fabs((a)-(b)))<(FLOAT_EPS))

再来一个例子证明精度问题,

#include <stdio.h>
#include <stdlib.h>
/***************************************
*Author:(公众号:最后一个bug)
****************************************/
#define	FACTOR	(1UL<<28)
int main(int argc, char *argv[]) {
	float a = 1.7 * FACTOR;
	float b = 0.2 * FACTOR;
	float c = a + b;

	float d = 1.9 * FACTOR;

	printf("%f\n", c);
	printf("%f\n", d);

	printf("%f\n",c - d);
	return 0;
}

打印结果为

510027392.000000
510027360.000000
32.000000

 
 

浮点数编程案例

#include <stdio.h>
#include <stdlib.h>

typedef union _tag_FloatConvert
{
    unsigned char byte[4];
    float Result;
}uFloatConvert;
/***************************************
*Author:(公众号:最后一个bug)
****************************************/
int main(int argc, char *argv[]) {
    uFloatConvert unFloatConvert; 
    float fVal   = 4.25;
    int   iVal   = 0x40880000;
    float *pfVal = NULL; 
    int   *pIVal = NULL; 

    //1)常见错误 
    fVal  = (float)iVal;
    printf("fVal = %.3f\n",fVal);
    printf("iVal = %d\n",iVal);

    //2) 正确做法
    pfVal = (float*)(&iVal);
    printf("*pfVal = %.3f\n",*pfVal);

    //3)采用共联体进行数据转化
    unFloatConvert.byte[0] = 0x00;
    unFloatConvert.byte[1] = 0x00;
    unFloatConvert.byte[2] = 0x88;
    unFloatConvert.byte[3] = 0x40;
    printf("unFloatConvert.Result = %.3f\n",unFloatConvert.Result);
    printf("公众号:最后一个bug\n");
    return 0;
}

运行结果

fVal = 1082654720.000
iVal = 1082654720
*pfVal = 4.250
unFloatConvert.Result = 4.250
公众号:最后一个bug

可见直接强制转换的做法是错误的!
 
 
本文内容大量参考下边的文章,如果有侵权问题,请联系我删除。
 
 

浮点数运算不满足交换律

(3.14 + 1e10) - 1e10 求值得到 0.0 因为尾数被丢掉了。
3.14 + (1e10 - 1e10) 会得到 3.14。
 
 

编译器、CPU不一定使用的是IEEE 754

x86 CPU 是在 x87 FPU的内部以80b计算得到结果后,再 truncate 为 IEEE 754 的 float(32b) 或者 double(64b),这中间会有微小的差异。

另一方面,ARM VFP 指令(IEEE 754 相容) 与 NEON指令(非完全 IEEE 754 相容)也可能会有不同的输出结果。

另外还有 gcc 的 -mfpmath=sse2 参数。
 
 

参考:
为什么 0.1 + 0.2 不等于 0.3 ?
【典藏】别怪"浮点数"太坑(C语言版本)
同事用"两个浮点数相等" 被说了一顿
浮点数的美丽与哀愁

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值