浮点数内存表示---记录一道题目
一、进制基础
1. 十进制转二进制的方法
十进制转换为二进制数时,由于整数与小数的转换方式不同,所以分别转换整数部分和小数部分再加以合并。例如将十进制整数转为二进制:把 (173)10 转换为二进制数。
例如将十进制小数转为二进制:把(0.8125)10转换为二进制小数。
十进制小数转换成二进制小数采用"乘2取整,顺序排列"法。具体做法是:用2乘十进制小数,可以得到积,将积的整数部分取出,再用2乘余下的小数部分,又 得到一个积,再将积的整数部分取出,如此进行,直到积中的整数部分为零,或者整数部分为1,此时0或1为二进制的最后一位。或者达到所要求的精度为止。
二、浮点数的二进制存储方式及转换
无论是单精度还是双精度在内存中的存储中都分为三个部分:
1.符号位:用0表示正,1表示负;
2.指数位:用于存储科学计数法中的指数数据,并且采用了移位存储;
3.尾数部分:由于科学计数法中的默认第一位总是1,因此可以被舍去。例如1.01*2^2(红色的1是默认位)。
float在内存中的存储方式如下:
而双精度的内存存储方式如下:
下面记录如何将float类型转化为内存存储格式的步骤:
1.先将实数转为二进制表示。
2.将这个二进制格式实数的小数点左移或者右移n位,使得小数点移动到第一个有效数字的右边。
3.如果实数是正的则31位放入0,如果是负的31位放入1。
4.科学计数法中的指数是可以出现负数的,所以规定,指数的真实值必须再加上一个中间数,对于8位的指数,这个中间数是127;对于11位的指数,这个中间数是1023。将加后的二进制数填入22-30位。
5.最后从小数点右边第一位开始数出23位数字放入到0-22位。
三、举两个例子
例1:float型浮点数125.5转化成32位二进制浮点数
125.5的二进制码为1111101.1,写成二进制的科学计数为:1.111101*2^6(因为科学计数法“整数”部分大于1,在二进制中,“整数”部分只能恒为1) 即向左移6位,则e=6,则E=e+127=133,而E的二进制码为10000101。
所以125.5的32位二进制浮点数为
0 10000101 11110100000000000000000
例2:float型浮点数0.5转化成32位二进制浮点数
0.5的二进制码为0.1,写成二进制的科学计数为:1.0*2^(-1)即向右移1位,则e=-1,则E=e+127=126,而E的二进制码为01111110。
所以0.5的32位二进制浮点数为
0 01111110 00000000000000000000000
例3:浮点数0.0f转化为32位二进制浮点数
其实关于0.0f有两种表达形式一个是-0.0f,一个是0.0f,而他们各自的二进制表示是:
-0.0f ----> 1 00000000 00000000000000000000000
0.0f ----> 0 00000000 00000000000000000000000
下面有一段程序验证之前所说的转化的规则:
#include<stdio.h> void main() { float x=-0.0f; char *p=(char*)&x; for (int i;=0; i<sizeof(x); ++i) printf("%d ",*p++); }
程序会打印出:“0 0 0 -128 ”,这里又想记录另一个概念叫大端模式和小端模式。
小端模式指的是数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低字节中。一般的Intel采用的是小端模式(据说)。数据的二进制表示从左往右就是从高到低字节。而我们知道指针是不断自增来访问下一个地址,所以高地址是后访问到。所以p所获取的二进制数分别是:
地址 --- 二进制数据
0x01---00000000
0x02---00000000
0x03---00000000
0x04---10000000
并且(10000000)2=(-128)10
最后是这个题目引发的这些的思考。
#include <iostream> #include <stdio.h> #include <string.h> #include <conio.h> using namespace std; int main(){ float a = 1.0f; cout << (int)a << endl; cout << &a << endl; cout << (int&)a << endl; }
输出如下: