IEEE 754浮点数转换

一个小数的二进制是怎么样的呢?我们先看看一个二进制的小数怎么转换成十进制:

11101.0101110=1×24+1×23+1×22+0×21+1×20+0×2−1+1×2−2+1×2−3+1×2−4+1×2−5=16+8+4+0+1+0+12+0+116+132=29.34375

(1)

IEEE 754

IEEE 754 标准中规定了浮点数在计算机中的表示方法,主要就是单精度(float)和双精度(double):

              S Exp      Fraction
Single(32bit) ▯▮▮▮▮▮▮▮▮▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯
Double(64bit) ▯▮▮▮▮▮▮▮▮▮▮▮▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯

其计算公式为:

𝑥=(−1)×(1+𝐹𝑟𝑎𝑐𝑡𝑖𝑜𝑛)×2(𝐸𝑥𝑝𝑜𝑛𝑒𝑛𝑡−𝐵𝑖𝑎𝑠)

(2)

其中,Bias为指数偏移值,是一个固定值,即𝐵𝑖𝑎𝑠=2𝑒−1−1 其中e为指数部分的比特长度。

举个例子,刚才我们算出来的小数可以这样表示:

29.34375=11101.01011=11101.010112×20=1.1101010112×24=(−1)0×(1+0.1101010112)×2131−127=(−1)0×(1+0.1101010112)×2100000112−127=(−1)0×(1+0.1101010112)×21027−1023=(−1)0×(1+0.1101010112)×2100000000112−127

(3)

因此在计算机中,表示为:

Float:
▯▮▮▮▮▮▮▮▮▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯
010000011110101011..............

Double:
▯▮▮▮▮▮▮▮▮▮▮▮▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯
010000000011110101011...........................................

不足的部分补上0,即:01000001111010101100000000000002=41𝑒𝑎𝑐00016

除了正常的浮点数外,还有几个比较特殊的:

十进制与二进制转换

计算方式为,将小数的整数部分与2取余倒序排列;将小数部分与2取整正序排列。例如,将3.14转换为float:

舍入操作

对于尾数多余精度的情况,需要舍去多余的部分,但不是按照四舍五入的方式,而是按照“向偶舍入”的方式,意思就是,如果多余的部分大于0.5(0.510=0.12)则最低位进1;如果小于0.5则舍去;如果正好是等于0.5则根据最低位判断,如果最低位是1则进位,否则舍去。这样按照统计学来看,对于一个小数有相同的机会进位或者被舍去。

例如对于上例中的3.14,我们可以得到:

3.14=11.001000111101011100001010001111010...2=1.1001000111101011100001010001111010...2×21=(−1)0+(1+0.1001000111101011100001010001111010...2)×2128−127=(−1)0+(1+0.1001000111101011100001010001111010...2)×2100000002−127≈(−1)0+(1+0.100100011110101110000112)×2100000002−127

(4)

因此3.14的float表示为:

▯▮▮▮▮▮▮▮▮▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯▯
01000000010010001111010111000011

还原

那么,对于一个存储在磁盘上的浮点数,我们怎么将它加载到内存中来?对于C来说,实际也是采用的IEEE754标准(float, double),所以实际上浮点数在内存中的表示是一致的,直接转换即可:

struct ConstantFloat {
        mutable u4 bytes;
        float &getValue() const {
            return *reinterpret_cast<float *>(&bytes);
        }
    };

参考: