浮点数探秘

浮点数探秘

浮点数在计算机中用以近似表示任意某个实数。具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到。

小数如何转二进制

整数部分

  • 方法1 整数部分除以2取余倒着写
59/2    **** ***1
29/2     *** ***1
14/2      ** ***0
 7/2       * ***1

 3/2         ***1
 1/2          **1
 0/2           *0
 0/2            0

59的二进制表示为0011 1011;

  • 方法2 二进制分解法
    把一个十进制数转成多个2的整数次方之和,然后分别转换成二进制,最后把所有二进制合并;
54 = 2^5 + 2^4 + 2^2 + 2^1
   = 0010 0000 + 0001 0000 + 0000 0100 + 0000 0010
   = 0011 0110

小数部分

乘以2取整法

如0.25转二进制

0.25*2=0.5  0
0.5*2 =1.0  1

即0.25转二进制为01

如0.4转二进制

0.4*2 =0.8  0
0.8*2 =1.6  1
0.6*2 =1.2  1
0.2*2 =0.4  0
...

即0.4转为二进制为0110 0110 ....,即二进制描述小数时无法做到绝对精确;

浮点数的表示方法

根据国际标准IEEE 754,任意一个二进制浮点数V可以表示成如下形式
(-1)^S * M * 2^E
其中

  • (-1)^S 表示符号位,当S=0,V为正,当S=1,V为负数。
  • M 表示有效数字,[1,2)
  • 2^E 表示指数,以2为底数。

例如十进制中的5.0写成二进制浮点数是101.0,用该形式表示就是 (-1)^0 * 1.01 * 2^2
其中S=0M=1.01E=2

又如十进制中的-5.5,写成二进制浮点数是101.1,用该形式表示就是(-1)^1 * 1.011 * 2^2
其中S=1M=1.011E=2

浮点数在内存中的表示

根据IEEE754标准规定:
对于32位的浮点数(float型),最高的一位是符号位S,接下里8位是指数E,剩下的23位为有效数字M。
对于64位的浮点数(double型),最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

┌─────────────┬──────────────────┬────────────────────┬─────────────────────┐
│    type     │   S(sign bit)    │  E(Exponent area)  │  M(Mantissa area)   │
├─────────────┼──────────────────┼────────────────────┼─────────────────────┤
│    float    │   1 bit(31bit)   │  8 bits(23-30bit)  │  23 bits(0-22bit)   │
├─────────────┼──────────────────┼────────────────────┼─────────────────────┤
│    double   │   1 bit(63bit)   │ 11 bits(52-62bit)  │  52 bits(0-51bit)   │
└─────────────┴──────────────────┴────────────────────┴─────────────────────┘

float与double类型的数据在计算机内部的表示法是相同的,但由于所占存储空间不同,分别能够表示的数据值范围和精度不同。

符号位S

对于符号位,只有0和1两种情况,分别表示正和负。

有效数字M

对于有效数字M,由于M的范围是[1,2),也就是说M的整数部分一定是1,所以IEEE754标准规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以舍去,只保存后面的小数部分。
比如保存1.01的时候,只保存小数部分01,而将整数部分舍去,等到读取的时候,再把第一位的1加上去。
这样做的目的是节省1位有效数字。
32位浮点数留给M只有23位,将第一位舍去后,就可以保留24位有效数字。

有效数字M位数决定数据的精度

  • float:2^23 = 8388608,共7位,最多能有7位有效数字; float 的精度为6-7位有效数字(能保证6位);
  • double:2^52 = 4503599627370496,共16位,最多能有16位有效数字;double的精度为15-16位有效数字;

指数部分E

  1. 指数部分E是一个无符号整数
  • 如果E为8位(float类型),那么E能表示的范围是0-255,
  • 如果E为11位(double类型),那么E能表示的范围是0-2047;
    这个指数E显然可以为负,但unsigned int的类型使E为非负数。
    所以IEEE754标准规定,存入内存时,真实的指数必须加上一个中间值(8位的E中间值为127,11位的E中间值为1023)。

如一个float数的E=3,那么存入内存时要加上127编程130后,再换算成二进制即1000 0010后存储。

  1. E不全为0或不全为1
    对于浮点数5.0
  • S=0,直接存储;
  • M=1.01,舍去整数1,将小数部分01存储,后面多余的位用0补齐;
  • E=2,需要加上127变成129并转换为二进制后存储区;

则5.0最终二进制表示为 0-100 0000 1-010 0000 0000 0000 0000 0000
以16进制展示即是40 A0 00 00

对于浮点数-5.5

  • S=1,直接存储;
  • M=1.011,舍去整数1,将小数部分011存储,后面多余的位用0补齐;
  • E=2,需要加上127变成129并转换为二进制后存储区;

则5.0最终二进制表示为 1-100 0000 1-011 0000 0000 0000 0000 0000
以16进制展示即是C0 B0 00 00

┌─────────────┬──────────────────┬────────────────────┬─────────────────────┐
│    value    │   S(sign bit)    │  E(Exponent area)  │  M(Mantissa area)   │
├─────────────┼──────────────────┼────────────────────┼─────────────────────┤
│     5.0     │        0         │   100 0000 1       │  010 0000 ...       │
├─────────────┼──────────────────┼────────────────────┼─────────────────────┤
│    -5.5     │        1         │   100 0000 1       │  011 0000 ...       │
└─────────────┴──────────────────┴────────────────────┴─────────────────────┘
#include <stdio.h>
int main()
{
    float f1 = 5.0;
    float f2= -5.5;
    printf("%f, 0x%x\n", f1, *(unsigned int*)&f1); // 5.000000, 0x40a00000
    printf("%f, 0x%x\n", f2, *(unsigned int*)&f2); // -5.500000, 0xc0b00000
    return 0;
}
  1. E全为0时
    以浮点数为例。
    由于E加上127后全为0,也就是说E的真实值为-127,即该浮点数指数部分是2^(-127),这是一个极小的数,此时有效数字M不再加上第一位的1,而是还原为以0位整数的小数。
    这样做是为了表示0,以及接近于0的很小的数字。
    双精度浮点同理。
#include <stdio.h>
void show_binary(const float f) {
    unsigned int num = *(unsigned int*)&f;
    printf("%.6f, 0x%X: ", f, num);

    const size_t max_size = 8 * sizeof(float);
    int i = (int)max_size;
    while (0 <= --i) {
        printf("%c", ((num >> i) & 0x1) + '0');
        if (0 == (i%4)) {
            printf(" ");
        }
    }
    printf("\n");
}
int main()
{
    float f21 = 5.0f;
    float f22= -5.5f;
    float f31 = 0.0f;
    float f32 = 0.000001f;
    show_binary(f21); //  5.000000, 0x40A00000: 0100 0000 1010 0000 0000 0000 0000 0000 
    show_binary(f22); // -5.500000, 0xC0B00000: 1100 0000 1011 0000 0000 0000 0000 0000 
    show_binary(f31); //  0.000000, 0x0: 0000 0000 0000 0000 0000 0000 0000 0000 
    show_binary(f32); //  0.000001, 0x358637BD: 0011 0101 1000 0110 0011 0111 1011 1101
    return 0;
}
  1. E全为1时
    以浮点数为例。
    由于E加上127后全为1,也就是说E的真实值是128,即该浮点指数部分是2^128,显示这是一个极大的数,此时表示正负无穷(正负由S决定)。
    双精度浮点同理。

指数E部分的位数决定了可表示的数据范围
占4个字节的int类型的范围:[-2^31,2^31-1]
占4个字节的float类型的范围:大约是[-3.4*10^38,3.4*10^38],即(-2^128,+2^128)

为何int和float都占4个字节的内存,float却比int表示的范围大得多呢?

秘密

  • float能表示的具体数字个数与int相同
  • float可表示的数字之间是不连续的,存在跳跃
  • float只是一种近似的表示法,不能作为精确数使用
  • 由于内存表示法相对复杂,float的运算速度比int慢很多

小结

  • 浮点类型与整数类型的内存表示法不同
  • 浮点类型的内存表示更复杂
  • 浮点类型可表示的范围更大
  • 浮点类型是一种不精确的类型
  • 浮点类型的运算速度较慢
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值