整数转浮点数精度溢出的原因和处理方式

       对于64位浮点数来说,其尾数有53位(包含首位的隐藏位),当一个整数转为浮点数时,只有当该整数的二进制位数不超过53位时,64位浮点数才可以精确的表示该整数,不然会造成精度丢失。试想一下,当一个十进制整数的二进制有54位,那么最后一位无法准确储存,64位浮点数就会造成精度的丢失,自然地,当位数超过53位后,64位浮点数的可表示整数并不是连续的。

       在笔者的这篇文章《浮点数的各种最值推算以及对python sys.float_info的解释》中,讲过浮点数能在该范围内精确表示的最大十进制整数为9007199254740991。那么如果一个整数大于该数,即其二进制位数超过53位,浮点数是怎么表示的呢?即当一个整数转成浮点数时,如果造成了精度溢出,那么浮点数对此是怎么处理的呢?
       首先我们来看一个简单的精度丢失的例子,如下所示。d就是9007199254740991,然后我们逐渐增加1,发现d+2,d+4,d+6的浮点数表示都是有误差的,即造成了精度丢失。

d=9007199254740991
float(d+1)
[out]:
9007199254740992.0

float(d+2)
[out]:
9007199254740992.0

float(d+3)
[out]:
9007199254740994.0

float(d+4)
[out]:
9007199254740996.0

float(d+5)
[out]:
9007199254740996.0

float(d+6)
[out]:
9007199254740996.0

       从上面例子可以看到,d+2,d+4,d+6是有误差的,但是d+1,d+3,d+5是没有误差的。有的有误差,有的没有误差,而且对于有误差的来说,有的是偏小,有的是偏大的,比如d+4是偏大的,而d+6是偏小的,这和浮点数对其的特殊处理方式有关,下面我们来说明这是为什么。

       我们截取上面数字二进制的最后五位来进行说明。对于d,其最后五位为11111,但是对于d+1,其变成了54位,并且首位是1,其他全是0,所以其最后五位是00000,同样的d+2为00001,d+3为00010,d+4为00011,d+5为00100,d+6为00101。除了d本身,其他的数字的最后一位都是超出了53位的,排在第54位,因此无法被64位浮点数储存。首先对于第54位是0的数字,我们发现64浮点数对其表示是准确的,并没有误差,这是因为虽然64位浮点数尾数不够,但是其指数是足够的,而指数对二进制的作用类似十进制一样,最后是在尾数的有效数字后面添加0,因此对于第54位是0的数字,浮点数可以准确表示,这就是为什么对d+1,d+3和d+5的表示没有误差的原因。

       但对于d+2,d+4和d+6,我们发现d+2和d+6都是偏小1,d+4是偏大1的。这是因为,当浮点数无法准确表示一个整数时,其约定,将该整数近似到一个最靠近该整数本身的可以表示的浮点数,因为最靠近一个整数的数,那么只有偏大最靠近和偏小最靠近两种情况,如果偏大最靠近和偏小最靠近距离该整数一样近,则选取让最低位(即第53位)为0的那个浮点数。比如对于d+2,其最后五位为00001,那么偏大最靠近的数为00010,偏小最靠近的数为00000,由于这种距离00001都一样近的情形选择的是让第53位为0的情形,所以浮点数最终表示的是0000+0,最后一个0通过指数的移位得到,所以最终得到的数是偏小1的;同理的,对于d+4,偏大最靠近是00100,偏小最靠近是00010,所以选择的是00100,所以最终表示的是偏大1,;对于d+6,偏大最靠近是00110,偏小最靠近是00100,所以选择的是00100,所以最终表示的是偏小1。

       最后需要注意的是,以上举的例子都是54位的,如果整数更大,超过的位数更多,那么偏大最靠近和偏小最靠近的可表示浮点数就不仅仅是相差1了,很可能会造成更大的误差,因具体的数字而已。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章