定点数与浮点数区别.docx
- 文档编号:25010390
- 上传时间:2023-06-03
- 格式:DOCX
- 页数:10
- 大小:22.25KB
定点数与浮点数区别.docx
《定点数与浮点数区别.docx》由会员分享,可在线阅读,更多相关《定点数与浮点数区别.docx(10页珍藏版)》请在冰豆网上搜索。
定点数与浮点数区别
定点数与浮点数区别
最近做HDR时,经常要用NV提供的16位纹理,它的说明书16位能到达24位的精度,就很奇怪?
一直搞不懂浮点数的精度怎么算的?
今天认真看了一下IEEEfloatpoint的标准,终于明白是什么了
1.什么是浮点数
在计算机系统的开展过程中,曾经提出过多种方法表达实数。
典型的比方相对于浮点数的定点数〔FixedPointNumber〕。
在这种表达方式中,小数点固定的位于实数所有数字中间的某个位置。
货币的表达就可以使用这种方式,比方99.00或者00.99可以用于表达具有四位精度〔Precision〕,小数点后有两位的货币值。
由于小数点位置固定,所以可以直接用四位数值来表达相应的数值。
SQL中的NUMBER数据类型就是利用定点数来定义的。
还有一种提议的表达方式为有理数表达方式,即用两个整数的比值来表达实数。
定点数表达法的缺点在于其形式过于僵硬,固定的小数点位置决定了固定位数的整数部分和小数部分,不利于同时表达特别大的数或者特别小的数。
最终,绝大多数现代的计算机系统采纳了所谓的浮点数表达方式。
这种表达方式利用科学计数法来表达实数,即用一个尾数〔Mantissa〕,一个基数〔Base〕,一个指数〔Exponent〕以及一个表示正负的符号来表达实数。
比方123.45用十进制科学计数法可以表达为1.2345×102,其中1.2345为尾数,10为基数,2为指数。
浮点数利用指数到达了浮动小数点的效果,从而可以灵敏地表达更大范围的实数。
提示:
尾数有时也称为有效数字〔Significand〕。
尾数实际上是有效数字的非正式说法。
同样的数值可以有多种浮点数表达方式,比方上面例子中的123.45可以表达为12.345×101,0.12345×103或者1.2345×102。
因为这种多样性,有必要对其加以标准化以到达统一表达的目的。
标准的〔Normalized〕浮点数表达方式具有如下形式:
±d.dd.d×βe,〔0≤diβ〕
其中d.dd.d即尾数,β为基数,e为指数。
尾数中数字的个数称为精度,在本文中用p来表示。
每个数字d介于0和基数之间,包括0。
小数点左侧的数字不为0。
基于标准表达的浮点数对应的详细值可由下面的表达式计算而得:
±〔d0+d1β-1+.+dp-1β-〔p-1〕〕βe,〔0≤diβ〕
对于十进制的浮点数,即基数β等于10的浮点数而言,上面的表达式非常容易理解,也很直白。
计算机内部的数值表达是基于二进制的。
从上面的表达式,我们可以知道,二进制数同样可以有小数点,也同样具有类似于十进制的表达方式。
只是此时β等于2,而每个数字d只能在0和1之间取值。
比方二进制数1001.101相当于1×23+0×22+0×21+1×20+1×2-1+0×2-2+1×2-3,对应于十进制的9.625。
其标准浮点数表达为1.001101×23。
2.IEEE浮点数
计算机中是用有限的连续字节保存浮点数的。
保存这些浮点数当然必须有特定的格式,Java平台上的浮点数类型float和double采纳了IEEE754标准中所定义的单精度32位浮点数和双精度64位浮点数的格式。
注意:
Java平台还支持该标准定义的两种扩展格式,即float-extended-exponent和double-extended-exponent扩展格式。
这里将不作介绍,有兴趣的读者可以参考相应的参考资料。
在IEEE标准中,浮点数是将特定长度的连续字节的所有二进制位分割为特定宽度的符号域,指数域和尾数域三个域,其中保存的值分别用于表示给定二进制浮点数中的符号,指数和尾数。
这样,通过尾数和可以调节的指数〔所以称为"浮点"〕就可以表达给定的数值了。
详细的格式参见下面的图例:
在上面的图例中,第一个域为符号域。
其中0表示数值为正数,而1那么表示负数。
第二个域为指数域,对应于我们之前介绍的二进制科学计数法中的指数部分。
其中单精度数为8位,双精度数为11位。
以单精度数为例,8位的指数为可以表达0到255之间的255个指数值。
但是,指数可以为正数,也可以为负数。
为了处理负指数的情况,实际的指数值按要求需要加上一个偏向〔Bias〕值作为保存在指数域中的值,单精度数的偏向值为127,而双精度数的偏向值为1023。
比方,单精度的实际指数值0在指数域中将保存为127;而保存在指数域中的64那么表示实际的指数值-63。
偏向的引入使得对于单精度数,实际可以表达的指数值的范围就变成-127到128之间〔包含两端〕。
我们不久还将看到,实际的指数值-127〔保存为全0〕以及+128〔保存为全1〕保存用作特殊值的处理。
这样,实际可以表达的有效指数范围就在-127和127之间。
在本文中,最小指数和最大指数分别用emin和emax来表达。
图例中的第三个域为尾数域,其中单精度数为23位长,双精度数为52位长。
除了我们将要讲到的某些特殊值外,IEEE标准要求浮点数必须是标准的。
这意味着尾数的小数点左侧必须为1,因此我们在保存尾数的时候,可以省略小数点前面这个1,从而腾出一个二进制位来保存更多的尾数。
这样我们实际上用23位长的尾数域表达了24位的尾数。
比方对于单精度数而言,二进制的1001.101〔对应于十进制的9.625〕可以表达为1.001101×23,所以实际保存在尾数域中的值为00110100000000000000000,即去掉小数点左侧的1,并用0在右侧补齐。
值得注意的是,对于单精度数,由于我们只有24位的指数〔其中一位隐藏〕,所以可以表达的最大指数为224-1=16,777,215。
特别的,16,777,216是偶数,所以我们可以通过将它除以2并相应地调整指数来保存这个数,这样16,777,216同样可以被准确的保存。
相反,数值16,777,217那么无法被准确的保存。
由此,我们可以看到单精度的浮点数可以表达的十进制数值中,真正有效的数字不高于8位。
事实上,对相对误差的数值分析结果显示有效的精度大约为7.22位。
参考下面的例如:
truevaluestoredvalue
--
16,777,2151.6777215E716,777,2161.6777216E716,777,2171.6777216E716,777,2181.6777218E716,777,2191.677722E716,777,2201.677722E716,777,2211.677722E716,777,2221.6777222E716,777,2231.6777224E716,777,2241.6777224E716,777,2251.6777224E7
--根据标准要求,无法准确保存的值必须向最接近的可保存的值进展舍入。
这有点像我们熟悉的十进制的四舍五入,即缺乏一半那么舍,一半以上〔包括一半〕那么进。
不过对于二进制浮点数而言,还多一条规矩,就是当需要舍入的值刚好是一半时,不是简单地进,而是在前后两个等距接近的可保存的值中,取其中最后一位有效数字为零者。
从上面的例如中可以看出,奇数都被舍入为偶数,且有舍有进。
我们可以将这种舍入误差理解为"半位"的误差。
所以,为了防止7.22对很多人造成的困惑,有些文章经常以7.5位来说明单精度浮点数的精度问题。
提示:
这里采用的浮点数舍入规那么有时被称为舍入到偶数〔RoundtoEven〕。
相比简单地逢一半那么进的舍入规那么,舍入到偶数有助于从某些角度减小计算中产生的舍入误差累积问题。
因此为IEEE标准所采用。
3.实数和浮点数之间的变换
如今我们已经明白了浮点数的IEEE表达方式。
我们来做些实数和浮点数之间的变换练习以加深理解。
在这些练习中,你还会发现一些围绕浮点数运算的令人吃惊的事实。
首先我们来看看事情简单的一面,从浮点数变换到实数。
理解了浮点数的格式,做这个练习并不难。
假定我们有一个32位的数据,用十六进制表示为0xC0B40000,并且我们知道它实际上是一个单精度的浮点数。
为了得到该浮点数实际表达的实数,我们首先将它变换为二进制形式:
C0B40000
11000000101101000000000000000000接着按照浮点数的格式切分为相应的域:
11000000101101000000000000000000符号域1意味着负数;指数域为129意味着实际的指数为2〔减去偏向值127〕;尾数域为01101意味着实际的二进制尾数为1.01101〔加上隐含的小数点前面的1〕。
所以,实际的实数为:
-1.01101×22
-〔20+2-2+2-32-5〕×22
-5.625从实数向浮点数变换略微费事一点。
假定我们需要将实数-9.625表达为单精度的浮点数格式。
方法是首先将它用二进制浮点数表达,然后变换为相应的浮点数格式。
首先,将小数点左侧的整数部分变换为其二进制形式,9的二进制性形式为1001。
处理小数部分的算法是将我们的小数部分乘以基数2,记录乘积结果的整数部分,接着将结果的小数部分继续乘以2,并不断继续该过程:
0.625×2=1.251
0.25×2=0.50
0.5×2=11
0当最后的结果为零时,完毕这个过程。
这时右侧的一列数字就是我们所需的二进制小数部分,即0.101。
这样,我们就得到了完好的二进制形式1001.101。
用标准浮点数表达为1.001101×23。
因为是负数,所以符号域为1。
指数为3,所以指数域为3+127=130,即二进制的10000010。
尾数省略掉小数点左侧的1之后为001101,右侧用零补齐。
最终结果为:
11000001000110100000000000000000最后可以将浮点数形式表示为十六进制的数据如下:
11000001000110100000000000000000C11A0000最终结果为0xC11A0000。
很简单?
等等!
你可能已经注意到了,在上面这个我们有意选择的例如中,不断的将产生的小数部分乘以2的过程掩盖了一个事实。
该过程完毕的标志是小数部分乘以2的结果为1,不难想象,很多小数根本不能经过有限次这样的过程而得到结果〔比方最简单的0.1〕。
我们已经知道浮点数尾数域的位数是有限的,为此,浮点数的处理方法是持续该过程直到由此得到的尾数足以填满尾数域,之后对多余的位进展舍入。
换句话说,除了我们之前讲到的精度问题之外,十进制到二进制的变换也并不能保证总是准确的,而只能是近似值。
事实上,只有很少一部分十进制小数具有准确的二进制浮点数表达。
再加上浮点数运算过程中的误差累积,结果是很多我们看来非常简单的十进制运算在计算机上却往往出人意料。
这就是最常见的浮点运算的"不准确"问题。
参见下面的Java例如:
System.out.print〔"34.6-34.0="+〔34.6f-34.0f〕〕;这段代码的输出结果如下:
34.6-34.0=0.5999985产生这个误差的原因是34.6无法准确的表达为相应的浮点数,而只能保存为经过舍入的近似值。
这个近似值与34.0之间的运算自然无法产生准确的结果。
4.特殊值
通过前面的介绍,你应该已经理解的浮点数的根本知识,这些知识对于一个不接触浮点数应用的人应该足够了。
不过,假设你兴趣正浓,或者面对着一个棘手的浮点数应用,可以通过本节理解到关于浮点数的一些值得注意的特殊之处。
我们已经知道,指数域实际可以表达的指数值的范围为-127到128之间〔包含两端〕。
其中,值-127〔保存为全0〕以及+128〔保存为全1〕保存用作特殊值的处理。
本节将详细IEEE标准中所定义的这些特殊值。
浮点数中的特殊值主要用于特殊情况或者错误的处理。
比方在程序对一个负数进展开平方时,一个特殊的返回值将用于标记这种错误,该值为NaN〔NotaNumber〕。
没有这样的特殊值,对于此类错误只能粗暴地终止计算。
除了NaN之外,IEEE标准还定义了±0,±∞以及非标准化数〔DenormalizedNumber〕。
对于单精度浮点数,所有这些特殊值都由保存的特殊指数值-127和128来编码。
假设我们分别用emin和emax来表达其它常规指数值范围的边界,即-126和127,那么保存的特殊指数值可以分别表达为emin-1和emax+1;。
基于这个表达方式,IEEE标准的特殊值如下所示:
其中f表示尾数中的小数点右侧的〔Fraction〕部分。
第一行即我们之前介绍的普通的标准化浮点数。
随后我们将分别对余下的特殊值加以介绍。
4.1.NaNNaN用于处理计算中出现的错误情况,比方0.0除以0.0或者求负数的平方根。
由上面的表中可以看出,对于单精度浮点数,NaN表示为指数为emax+1=128〔指数域全为1〕,且尾数域不等于零的浮点数。
IEEE标准没有要求详细的尾数域,所以NaN实际上不是一个,而是一族。
不同的实现可以自由选择尾数域的值来表达NaN,比方Java中的常量Float.NaN的浮点数可能表达为01111111110000000000000000000000,其中尾数域的第一位为1,其余均为0〔不计隐藏的一位〕,但这取决系统的硬件架构。
Java中甚至允许程序员自己构造具有特定位形式的NaN值〔通过Float.intBitsToFloat〔〕方法〕。
比方,程序员可以利用这种定制的NaN值中的特定位形式来表达某些诊断信息。
定制的NaN值,可以通过Float.isNaN〔〕方法断定其为NaN,但是它和Float.NaN常量却不相等。
实际上,所有的NaN值都是无序的。
数值比较操作符,=,和=在任一操作数为NaN时均返回false。
等于操作符==在任一操作数为NaN时均返回false,即使是两个具有一样位形式的NaN也一样。
而操作符!
=那么当任一操作数为NaN时返回true。
这个规那么的一个有趣的结果是x!
=x当x为NaN时竟然为真。
可以产生NaN的操作如下所示:
此外,任何有NaN作为操作数的操作也将产生NaN。
用特殊的NaN来表达上述运算错误的意义在于防止了因这些错误而导致运算的不必要的终止。
比方,假设一个被循环调用的浮点运算方法,可能由于输入的参数问题而导致发生这些错误,NaN使得即使某次循环发生了这样的错误,也可以简单地继续执行循环以进展那些没有错误的运算。
你可能想到,既然Java有异常处理机制,也容许以通过捕获并忽略异常到达一样的效果。
但是,要知道,IEEE标准不是仅仅为Java而制定的,各种语言处理异常的机制不尽一样,这将使得代码的迁移变得更加困难。
何况,不是所有语言都有类似的异常或者信号〔Signal〕处理机制。
注意:
Java中,不同于浮点数的处理,整数的0除以0将抛出java.lang.ArithmeticException异常。
4.2.无穷
和NaN一样,特殊值无穷〔Infinity〕的指数部分同样为emax+1=128,不过无穷的尾数域必须为零。
无穷用于表达计算中产生的上溢〔Overflow〕问题。
比方两个极大的数相乘时,尽管两个操作数本身可以用保存为浮点数,但其结果可能大到无法保存为浮点数,而必须进展舍入。
根据IEEE标准,此时不是将结果舍入为可以保存的最大的浮点数〔因为这个数可能离实际的结果相差太远而毫无意义〕,而是将其舍入为无穷。
对于负数结果也是如此,只不过此时舍入为负无穷,也就是说符号域为1的无穷。
有了NaN的经历我们不难理解,特殊值无穷使得计算中发生的上溢错误不必以终止运算为结果。
无穷和除NaN以外的其它浮点数一样是有序的,从小到大依次为负无穷,负的有穷非零值,正负零〔随后介绍〕,正的有穷非零值以及正无穷。
除NaN以外的任何非零值除以零,结果都将是无穷,而符号那么由作为除数的零的符号决定。
回忆我们对NaN的介绍,当零除以零时得到的结果不是无穷而是NaN。
原因不难理解,当除数和被除数都逼近于零时,其商可能为任何值,所以IEEE标准决定此时用NaN作为商比较适宜。
4.3.有符号的零
因为IEEE标准的浮点数格式中,小数点左侧的1是隐藏的,而零显然需要尾数必须是零。
所以,零也就无法直接用这种格式表达而只能特殊处理。
实际上,零保存为尾数域为全为0,指数域为emin-1=-127,也就是说指数域也全为0。
考虑到符号域的作用,所以存在着两个零,即+0和-0。
不同于正负无穷之间是有序的,IEEE标准规定正负零是相等的。
零有正负之分,确实非常容易让人困惑。
这一点是基于数值分析的多种考虑,经利弊权衡后形成的结果。
有符号的零可以防止运算中,特别是涉及无穷的运算中,符号信息的丧失。
举例而言,假设零无符号,那么等式1/〔1/x〕=x当x=±∞时不再成立。
原因是假设零无符号,1和正负无穷的比值为同一个零,然后1与0的比值为正无穷,符号没有了。
解决这个问题,除非无穷也没有符号。
但是无穷的符号表达了上溢发生在数轴的哪一侧,这个信息显然是不能不要的。
零有符号也造成了其它问题,比方当x=y时,等式1/x=1/y在x和y分别为+0和-0时,两端分别为正无穷和负无穷而不再成立。
当然,解决这个问题的另一个思路是和无穷一样,规定零也是有序的。
但是,假设零是有序的,那么即使if〔x==0〕这样简单的判断也由于x可能是±0而变得不确定了。
两害取其轻者,零还是无序的好。
4.4.非标准化数
我们来考察浮点数的一个特殊情况。
选择两个绝对值极小的浮点数,以单精度的二进制浮点数为例,比方1.001×2-125和1.0001×2-125这两个数〔分别对应于十进制的2.6448623×10-38和2.4979255×10-38〕。
显然,他们都是普通的浮点数〔指数为-125,大于允许的最小值-126;尾数更没问题〕,按照IEEE754可以分别保存为00000001000100000000000000000000〔0x1100000〕和00000001000010000000000000000000〔0x1080000〕。
如今我们看看这两个浮点数的差值。
不难得出,该差值为0.0001×2-125,表达为标准浮点数那么为1.0×2-129。
问题在于其指数大于允许的最小指数值,所以无法保存为标准浮点数。
最终,只能近似为零〔FlushtoZero〕。
这中特殊情况意味着下面本来非常可靠的代码也可能出现问题:
if〔x!
=y〕{
z=1/〔x-y〕;
}正如我们精心选择的两个浮点数展现的问题一样,即使x不等于y,x和y的差值仍然可能绝对值过小,而近似为零,导致除以0的情况发生。
为理解决此类问题,IEEE标准中引入了非标准〔Denormalized〕浮点数。
规定当浮点数的指数为允许的最小指数值,即emin时,尾数不必是标准化的。
比方上面例子中的差值可以表达为非标准的浮点数0.001×2-126,其中指数-126等于emin。
注意,这里规定的是"不必",这也就意味着"可以"。
当浮点数实际的指数为emin,且指数域也为emin时,该浮点数仍是标准的,也就是说,保存时隐含着一个隐藏的尾数位。
为了保存非标准浮点数,IEEE标准采用了类似处理特殊值零时所采用的方法,即用特殊的指数域值emin-1加以标记,当然,此时的尾数域不能为零。
这样,例子中的差值可以保存为00000000000100000000000000000000〔0x100000〕,没有隐含的尾数位。
有了非标准浮点数,去掉了隐含的尾数位的制约,可以保存绝对值更小的浮点数。
而且,也由于不再受到隐含尾数域的制约,上述关于极小差值的问题也不存在了,因为所有可以保存的浮点数之间的差值同样可以保存。
A16-bitfloating-pointnumberhasa1-bitsign〔S〕,a5-bitexponent〔E〕,anda10-bitmantissa〔M〕.Thevalueofa16-bitfloating-pointnumberisdeterminedbythefollowing:
〔-1〕^S*0.0,ifE==0andM==0,
〔-1〕^S*2^-14*〔M/2^10〕,ifE==0andM!
=0,
〔-1〕^S*2^〔E-15〕*〔1+M/2^10〕,if0E31,
〔-1〕^S*INF,ifE==31andM==0,orNaN,ifE==31andM!
=0,
whereS=floor〔〔Nmod65536〕/32768〕,
E=floor〔〔Nmod32768〕/1024〕,andM=Nmod1024.
因此当E=0时,按非标准浮点数处理得到的结果就2^-24精度的数字
MSN空间完美搬家到新浪博客!
特别声明:
1:
资料来源于互联网,版权归属原作者
2:
资料内容属于网络意见,与本账号立场无关
3:
如有侵权,请告知,立即删除。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 定点 浮点 区别