首页 > 编程知识 正文

浮点数在存储时存在舍入误差,浮点精度丢失

时间:2023-05-03 14:23:54 阅读:228447 作者:3796

本文主要对单精度浮点数的范围大小进行一些简单的讨论。

分为五部分:

一、单精度浮点数的取值范围

二、单精度浮点数在内存中的存储形式

三、单精度浮点数的范围及其实现原理的关系

四、单精度浮点数在计算中造成误差的原因

一、单精度浮点数的取值范围:

二、单精度浮点数在内存中的存储形式

MS是符号部分,1为负 0为正
E是阶码部分,为31位到第24位这8个二进制位,表示该实数转化为规格化的二进制实数后的指数与127(127即所谓偏移量)之和即所谓阶码.
M是尾数部分,为第22位到第0位,表示该实数转化为规格化的二进制实数后小数点以后的其余各位即所谓尾数.

那么150 .125(十进制)在内存中如何存储

第一步:求出其尾数部分

先将十进制数转化为二进制数并将其用科学计数法表示。

150.125(十进制)=10010110.001=1.0010110001*2^7

舍去小数点前的整数后,其小数部分0010110001并在后面补0直至位数达到23位,得到尾数部分

第二步:求出阶码部分

在第一步中的1.0010110001*2^7,得到指数7,那么其阶码为134(127+7),二进制形式为10000110

所以150.125在内存中的存储形式为:0  10000110 00101100010000000000000

三、单精度浮点数的范围及其实现原理的关系

在讨论这个问题之前需要提出几个前提的概念

概念1:normal number& subnormal number

根据IEEE754的规定, 按照尾数位隐藏的整数部分是 1 还是0可以将浮点数划分为两类: normal number和 subnormal number

normal number

就是尾数位隐藏的整数部分是1的数, 这种数叫做normal number, 可以理解为"正常的数",我们一般使用的单精度数为该种类型。

例子:

单精度数150.125在内存中的存储形式为:0  10000110 00101100010000000000000,

其尾数为00101100010000000000000舍去0010110001后的0,即0010110001,但其真正的尾数是1.0010110001,因为其整数部分被我们给舍去了。

这种数就为normal number。

subnormal number

尾数位隐藏的整数部分为0的数, 叫做subnormal number, 也叫作denormal number, 可以理解为"低于正常数的数"

IEEE754规定:  如果将阶码位全部填充为0, 则表示这个数是个subnormal number

当我们发现内存中阶码位全为0,那么这个数为subnormalnumber,并且其尾数舍去的整数部分部分为1。

这个概念的引入在浮点数下溢时, 可以逐位的损失精度, 以尽可能精确的表达0附近的极小数, 之后会具体讲解.

概念2: non-number

IEEE754规定:当阶码位全部被1填充, 即阶码表示的值为255时,那么这个数为non-number,表示这个值为±infinity或NaN(分别表示无穷Not a Number)。

同样的当我们发现内存中阶码位全为1,那么这个数为non-number。

至此我们可以作以下总结。

阶码位情况阶码位全为0

阶码在[1,254]

(十进制)

阶码位全为1浮点数的类型subnormal numbernormal numbernon-number

-127的阶码用于表示subnormal number(阶码位全为0),128的阶码用于表示non-number(阶码位全为1)

所以normal number的阶码范围[1,254],在减去偏移量127之后为[-126,127],实际上单精度浮点数的有效阶码的范围在[-126,127]

对于normal number,其舍去(隐藏)的整数部分数为1

而尾数位用于表示十进制数转化为二进制数的科学计数法中的小数位,所以尾数的表示范围在[1.00000......,1.11111.....](二进制)=[1,2)(十进制)

由于尾数位位数有限,所以尾数的最大值只能尽可能接近2,且永远不会等于2。

这里开始体现出浮点数对于绝大部分实数只能尽可能近似等于的思想,这也是导致浮点数运算会导致误差的原因之一,下一节还会对浮点数误差进行讨论。

至此,我们可以对浮点数的范围进行估计了:

范围=±尾数*2^阶码

       =±[1,2)*2^[-126,127]

       =(-2*2^127,-1*2^-126]∪[1*2^-126,2*2^127)

       =(-3.4028...*10^38,-1.1754...*10^-38]∪[1.1754...*10-38,34028...*10^38)

       取其子集:

       得到(-3.4*10^38,-1.8*10^-38]∪[1.8*10^-38,3.4*10^38)即为单精度浮点数范围

       四、单精度浮点数在计算中造成误差的原因 原因一:数值溢出

单精度浮点数数值溢出分为上溢出下溢出

上溢出:

|浮点数运算结果|>|浮点数所能表示的最大数|

下溢出:

|浮点数运算结果|<|浮点数所能表示的最大数|

****下溢出时候,系统将运算结果处理为0

(解决方法:估算数据精度后,自定义数据类型或者采用更高精度的数据类型)

 

 

原因二:类型转换

我们很多人包括我之前也是,认为取值范围小的数据类型向取值范围大的数据类型进行赋值认为是安全的,但并不是,虽然这样不会造成数值溢出,但是会造成数值精度的损失。

long a=123456789;

float b;

b=a;

这时候单精度类型b的结果会是123456792.000000.

我们引入有效数字的概念:

从左边第一个非0的数字起,到精确的位数为止,期间的有效数字。

我们发现单精度类型b的有效数字是7.

原因三:二进制小数与十进制小数之间并不是一一对应的关系 二进制小数十进制小数2^-230.000000119209289550781252^-22

0.0000002384185791015625

2^-210.0000004768371582031252^-200.000000953674316406252^-190.0000019073486328135.............

我们可以发现二进制小数是连续的,而且有与之一一对应的十进制小数。

但是十进制小数同样有实数的稠密性,在相邻两个二进制小数所对应的十进制小数之间还有无数个小数,而十进制小数不一定有其所对应的二进制小数,。

这就导致我们在为单精度值进行赋值的时候,所用十进制数,不一定能有与之对应的二进制数,那么计算机是如何处理的呢?

计算机会让我们输入的十进制数近似等于某一个二进制数,这就是浮点数出现误差的原因。这是由于数字的性质和浮点数的构成所导致的误差,是无法避免的,我们在给一个浮点数赋值一个十进制数时,有确切与之对应的二进制数的可能性接近于0,所以从宏观上来看浮点数的误差无法避免,我们只能在能够解决问题的前提下相应地控制浮点数的误差。

最后,谢谢大家的阅读,对单精度浮点数的有效数字为何等于7的问题,我也会近期赶出来。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。