IEEE754浮点格式简述 和 C语言基本数据类型转换实质
时间:2011-02-16 来源:xiaolizhi
没看过什么Prime,也不知道C编译器原理,而编译原理,汇编,组成原理只学点皮毛。
以下是成果,欢迎指正和释疑。
//编译环境:INTEL T3400处理器, XP 32系统, DEBUG模式编译运行,VS2008(VC9)
//IEEE 754
// 数符(s) 阶码(E) 尾数(M) 总位数 偏移值十六进制 偏移值十进制
//短实数 1位 8位 23位 32位 0x7FH +127
//长实数 1位 11位 52位 64位 0x3FFH +1023
//临时实数 1位 15位 64位 80位 0x3FFFH +16383
//
//说明: 格式比如 0 11111110 1111111 11111111 11111111
// 数符 阶码 尾数
//数符是尾数的符号,阶码是指数的真值加上偏移得到的二进制,尾数知实数和长实数隐藏了一个整数位
//也就是1111111 11111111 11111111其实是1.1111111 11111111 11111111,不是补码,可以认为是绝过值的原码
//临时实数不设隐藏位
//于是上面例子求解 + 2^(254 - 127) * (1 + 1 - 2^(-23)) = 3.4028234663852885981170418348452e+38
#include "stdio.h"
#include <float.h>
void main()
{
unsigned int o0 = 2147483649;//10000000 00000000 00000000 00000001
int p0 = o0;//得10000000 00000000 00000000 00000001,原码 11111111 11111111 11111111 11111111
int o00 = 2147483649;//10000000 00000000 00000000 00000001
unsigned int p00 = o00;//得10000000 00000000 00000000 00000001
printf("%u, %d\n",o0,p0); //2147483648, -2147483648
printf("%d, %u\n",o00,p00); //-2147483648, 2147483648
//看样子是直接二进制复制,把复制得到的二十进重新解释
int o = 0xFFFF4000; //11111111 11111111 01000000 00000000
short p = o; //得01000000 00000000
short q = *((short *)(&o)); ////得01000000 00000000
printf("%d, %hd, %hd\n",o,p,q); //-49152, 16384, 16384
//看样子是直接截断获取低16位,也就是内存前两字节(我们的电脑一般都是little endian存储)
int o1 = 0xFFFF8000; //11111111 11111111 10000000 00000000
short p1 = o1; //得10000000 00000000
short q1 = *((short *)(&o1)); //得10000000 00000000
printf("%d, %hd, %hd\n",o1,p1,q1); //-32768, -32768, -32768
//同上
short a1 = 32767; //01111111 11111111
int b1 = (unsigned short)a1; //得00000000 00000000 01111111 11111111
int c1 = a1; //得00000000 00000000 01111111 11111111
int d1 = *((int *)&a1);
//得11001100 11001100 01111111 11111111 这高16位哪来的?
//a1所占内存的后面两个字节,为什么是这两个,编译的结果,本人不懂深层
printf("%hd, %d, %d, %d\n",a1,b1,c1,d1); //32767, 32767, 32767, -859013121
short a = 32768; //得10000000 00000000
int b = (unsigned short)a; //得00000000 00000000 10000000 00000000
int c = a; //得11111111 11111111 10000000 00000000
int d = *((int *)&a); //11001100 11001100 10000000 00000000 跟上面的情况一样,又是这样。
printf("%hd, %d, %d, %d\n",a,b,c,d); //-32768, 32768, -32768, -859013120
//对比上面跟这一段代码得出,如果原来是正数(无符号的一定是正数),高位补0,负数则补1,
//这有点像补码正数右移补0,负数右移补1,当然实际是编译技术干的,不懂深层
long x = 2147483648; //10000000 00000000 00000000 00000000
float y = (unsigned long)x; //0 10011110 0000000 00000000 00000000 IEEE 754标准
float y1 = *((float *)(&x)); //1 00000000 0000000 00000000 00000000 IEEE 754标准 得到 -0
float z = x; //1 10011110 0000000 00000000 00000000 00000000 IEEE 754标准
printf("%ld, %f, %f, %f\n",x,y,y1,z); //-2147483648, 2147483648.000000, -0.000000, -2147483648.000000
//看样子跟整数转换原理差不多,如果是取地址转换指针类型,再间址,就直接二进制赋值,也就是直接把x对应的存储区重新解释为浮点,
//因为等号两边数据类型一样了,只要直接二进制赋值就行了,不用再进行什么特别处理了。
//什么数据类型在内存里本质都是二进制,包括代码,都是二进制,只有程序运行的时候才产生了代码和不同类型的数据的逻辑上的不同。
//如果是直接值等于,则会特别处理,如果原来是正数,则求出二进制表示的真值,转换成IEEE 754标准的正数,如果是负转换为对应浮点负数
//估计又是编译技术实现的
float l = FLT_MAX;
//3.402823466e+38F ---> 0 11111110 1111111 11111111 11111110 --误差(为什么误差成这样,本人不知道)-->
//340282346638528860000000000000000000000.000000--> 0 11111110 1111111 11111111 11111111
//(另外怎么指数最大不是11111111而是11111110,网上说是IEEE 754约定)
unsigned long m = (unsigned long)l; //00000000 00000000 00000000 00000000 //为什么,本人不知道
unsigned long m1 = *((unsigned long *)(&l)); //01111111 01111111 11111111 11111111 直接二进制赋值
long n = l; //10000000 0000000 00000000 00000000 //为什么,本人不知道
printf("%f, %lu, %lu, %ld\n",l,m,m1,n); //340282346638528860000000000000000000000.000000, 0, 2139095039, -2147483648
unsigned char * pl = (unsigned char *)&l; //11111111 11111111 01111111 01111111 这里是指内存低地址到高地址存储的数据
printf("%hu, %hu, %hu, %hu\n",pl[0],pl[1],pl[2],pl[3]); //255, 255, 127, 127
unsigned char str[4] = {255,255,127,127}; //11111111 11111111 01111111 01111111 这里是指内存低地址到高地址存储的数据
printf("%f\n",*((float *)str)); //340282346638528860000000000000000000000.000000
float ff = 1.0f; //0 01111111 0000000 00000000 00000000
printf("%lu\n",*((unsigned long*)&ff));
//1065353216 ------ 0 01111111 0000000 00000000 00000000 (IEEE 754浮点 1.0) -> 00111111 10000000 00000000 00000000 (整数 1065353216)
float i = 32769.5; //0 10001110 0000000 00000001 10000000 注意尾数部分的隐藏位
short j = i; //10000000 00000001
unsigned short k = i; //10000000 00000001
printf("%f, %hd, %hu\n",i,j,k); //32769.500000, -32767, 32769
//看来是先把小数部分去掉,然后得到整数直接赋给整数变量,实现深层原理不懂,直接内存重解析就不做,结果很明白的
//short转浮点估计跟前面long转float一样
float u = FLT_MAX;
//3.402823466e+38F ---> 0 11111110 1111111 11111111 11111110 --误差>340282346638528860000000000000000000000.000000-->
//0 11111110 1111111 11111111 11111111
double v = u;
//0 10001111110 11111111 11111111 11111110 00000000 00000000 00000000 0000
//尾数部分基本不变,就是最后一位有问题,阶码改变1023的偏移
double w = *((double *)&u);
//11001100 11001100 11001100 11001100 01111111 01111111 11111111 11111111
//又是11001100这玩意,01111111 01111111 11111111 11111111还是一样的(当然了,又没去改u的存储区的值),
//至于高16位,真不知道为什么是这些,但是肯定是u存储区后的数据
=
printf("*********************\n%f\n %lf\n %lf\n",u,v,w);
//输出
//*********************
//340282346638528860000000000000000000000.000000
// 340282346638528860000000000000000000000.000000
// -92559616541579665000000000000000000000000000000000000000000000.000000 奇怪,Release模式下的是 0.000000
unsigned char * pv = (unsigned char *)&v;//00000000, 00000000, 00000000, 11100000, 11111111, 11111111, 11101111, 01000111
printf("%hu, %hu, %hu, %hu, %hu, %hu, %hu, %hu\n",pv[0],pv[1],pv[2],pv[3],pv[4],pv[5],pv[6],pv[7]); //0, 0, 0, 224, 255, 255, 239, 71
//v二进制整理得 01000111 11101111 11111111 11111111 11100000 00000000 00000000 00000000
//正好是上面输出的相反
unsigned char * pw = (unsigned char *)&w; //11111111 11111111 01111111 01111111
printf("%hu, %hu, %hu, %hu\n",pw[0],pw[1],pw[2],pw[3]);//255, 255, 127, 127
double u1 = FLT_MAX; //0 10001111110 11111111 11111111 11111110 00000000 00000000 00000000 0000
float v1 = u1; //0 11111110 1111111 11111111 11111111 尾数基本不变,除了最后一位,阶码改变127的偏移
float w1 = *((float *)&u1);//1 11000000 0000000 00000000 00000000,整理11100000 00000000 00000000 00000000,正好是双精度浮点的低(前)32位
printf("*********************\n%lf\n %f\n %f\n",u1,v1,w1);
//*********************
//340282346638528860000000000000000000000.000000
// 340282346638528860000000000000000000000.000000
// -36893488147419103000.000000
double u2 = DBL_MAX;//0 11111111110 1111 11111111 11111111 11111111 11111111 11111111 11111111
float v2 = u2;//转换后不知是什么玩意了
float w2 = *((float *)&u2); //转换后不知是什么玩意了
printf("*********************\n%lf\n %f\n %f\n",u2,v2,w2);
//*********************
//17976931348623157000000000000000000000000000000000000000000000000000000000000000
//00000000000000000000000000000000000000000000000000000000000000000000000000000000
//00000000000000000000000000000000000000000000000000000000000000000000000000000000
//000000000000000000000000000000000000000000000000000000000000000000000.000000
// 1.#INF00
// -1.#QNAN0
unsigned char * pv2 = (unsigned char *)&v2; //00000000 00000000 10000000 01111111
printf("%hu, %hu, %hu, %hu\n",pv2[0],pv2[1],pv2[2],pv2[3]);//0, 0, 128, 127
unsigned char * pw2 = (unsigned char *)&w2; //11111111 11111111 11111111 11111111,显然是u2的低32位,反序,这里看不出来
printf("%hu, %hu, %hu, %hu\n",pw2[0],pw2[1],pw2[2],pw2[3]);//255, 255, 255, 255
//看到了,上面的v2和w2居然是这样的,不过都不是合法浮点数
//00000000 00000000 10000000 01111111 反过来就是 01111111 10000000 00000000 00000000,拆分成IEEE 754格式 0 11111111 0000000 00000000 00000000
//11111111 11111111 11111111 11111111 反过来就是 11111111 11111111 11111111 11111111,拆分成IEEE 754格式 1 11111111 1111111 11111111 11111111
//完整输出,DEBUG模式编译运行
//2147483649, -2147483647
//-2147483647, 2147483649
//-49152, 16384, 16384
//-32768, -32768, -32768
//32767, 32767, 32767, -859013121
//-32768, 32768, -32768, -859013120
//-2147483648, 2147483648.000000, -0.000000, -2147483648.000000
//340282346638528860000000000000000000000.000000, 0, 2139095039, -2147483648
//255, 255, 127, 127
//340282346638528860000000000000000000000.000000
//1065353216
//32769.500000, -32767, 32769
//*********************
//340282346638528860000000000000000000000.000000
// 340282346638528860000000000000000000000.000000
// -92559616541579665000000000000000000000000000000000000000000000.000000
//0, 0, 0, 224, 255, 255, 239, 71
//*********************
//340282346638528860000000000000000000000.000000
// 340282346638528860000000000000000000000.000000
// -36893488147419103000.000000
//*********************
//17976931348623157000000000000000000000000000000000000000000000000000000000000000
//00000000000000000000000000000000000000000000000000000000000000000000000000000000
//00000000000000000000000000000000000000000000000000000000000000000000000000000000
//000000000000000000000000000000000000000000000000000000000000000000000.000000
// 1.#INF00
// -1.#QNAN0
//0, 0, 128, 127
//255, 255, 255, 255
//对于浮点FLT_MAX(3.402823466e+38F):
//2139095039 = 01111111 01111111 11111111 11111111
//255, 255, 127, 127 因为小头存储所以方向反一下也是 01111111 01111111 11111111 11111111
//01111111 01111111 11111111 11111111
//0 11111110 1111111 11111111 11111111 IEEE 754拆分,其中第一个0表示数符为正
//0 01111111 1111111 11111111 11111111 阶码是真值的127偏移,减127得真值为127
//0 01111111 1 1111111 11111111 11111111 尾数部分包函有隐藏位,补上一个1
//+ 1.1111111 11111111 11111111 * 2 ^ 01111111 = (1 + 1 - 2^(-23)) * 2^127 = 3.4028234663852885981170418348452e+38
}
相关阅读 更多 +