浮点运算是如何在一个大数字上加一的



如果我们运行以下代码:

 #include <iostream>
 int main ()
 { 
     using namespace std;
     float a = 2.34E+22f;
     float b = a+1.0f;  
     cout<<"a="<<a<<endl;
     cout<<"b-a"<<b-a<<endl;
     return 0;
 }

那么结果将是0,因为浮点数只有6个有效数字。但浮点数1.0试图加到23位数字上。那个么,程序是如何实现数字1并没有位置的,算法是什么呢?

逐步:

IEEE-754 32位二进制浮点格式:

符号1位有效位23位指数8位

I) float a = 23400000000.f;

23400000000.f转换为float:

23400000000=101 0111 0010 1011 1111 1010 0000 00002=1.01011100101011111110101010000000002•234

但是有效位只能在该点之后存储23位。所以我们必须绕过:

1.01011100101011111110101 010000000002•234≈1.010111001010111111101012•234

因此,在之后

float a = 23400000000.f;

CCD_ 5等于23399991808。

II) float b = a + 1;

a=101011100101011111110101000000000002。b=10101011100101011111110101000000000012=1.01011100101011111110101000000000012•234

但是,同样,有效位只能在该点之后存储23个二进制数字。所以我们必须绕过:

1.01011100101011111110101 000000000012•234≈1.010111001010111111101012•234

因此,在之后

float b = a + 1;

CCD_ 7等于23399991808。

III) float c = b - a;

101011100101011111110101000000000002

该值可以存储在float中,而无需取整。

因此,在之后

float c = b - a;

CCD_ 10等于0。

基本原理是将两个数字对齐,使小数点位于同一位置。我用一个10位数的数字来让它更容易阅读:

 a = 1.234E+10f;
 b = a+1.0f;

计算+1.0f时,小数点需要排列:

 1.234E+10f becomes 1234000000.0
 1.0f       becomes          1.0
            + 
            =       1234000001.0

但由于它是浮动的,右边的1在有效范围之外,所以存储的数字将是1.234000E+10——超过这个数字的任何数字都会丢失,因为数字不够。

[注意,如果你在优化编译器上这样做,它可能仍然会显示1.0,因为浮点单元使用64位或80位的内部表示,所以如果计算是在不将中间结果存储在变量中的情况下完成的(一个好的编译器肯定可以在这里实现这一点)对于2.34E+22f,可以保证不适合64位浮点,也可能不适合80位浮点。]。

当将两个FP数字相加时,它们首先转换为相同的指数。以十进制表示:2.34000E+22 + 1.00000E0 = 2.34000E22 + 0.000000E+22。在该步骤中,1.0会因舍入而丢失。

二进制浮点的工作原理基本相同,只是E+22被2^77取代。

最新更新