• 浮点数在计算机的表示方法


    一个哥们在qq群里问了一个关于浮点数的程序,然后行了行浮点数的知识.竟然忘了,所有找了些文章.回忆回忆,理解理解

    首先来聊天他的问题和让我无言以对的解决办法吧

    ""十六进制转负数浮点数怎么转换啊"

    然后我默默的写了一个下面的东东

    #include "stdafx.h"
    #include <iostream>
    #include <stdio.h>
    #include <string>
    #include <vector>
    using namespace std;
    
    float getFloat()
    {
    	return (float)3.1423;
    }
    
    string FloatToHex ( float fNum )
    {
    	int nInteger = (int)(fNum);
    	int nPower = 0;
    	while( fNum - nInteger > 1e-5 )
    	{
    		fNum *= 10;
    		nInteger = (int)(fNum);
    		nPower++;
    	}
    	std::cout<<nPower<<std::endl;
    
    	return "";
    }
    string FloatLength ( float fNum )
    {
    	char cAryFloat[100] = {0};
        sprint(cAryFloat, "%g", fNum);//从字符串cAryFloat中统计小数点后面有几位!!! %f 3.142300 %g 3.1423 %e 31423 + 1e-4      return ""; }
    int _tmain(int argc, _TCHAR* argv[]) { FloatToHex(getFloat()); getchar(); return 0; } /* 在getFloat()函数中如果 return (float)3.14 那么最后打印的是2 在getFloat()函数中如果 return (float)3.1423 那么最后打印的是7 纠结了半天,猜测着可能你是返回3.1423但是你没办法控制float的精度,所有最后会打印7吧.
    float最后可能是31422.9999999 打印的时候打印出了31423.但是强转int的话int仍然是32422.
    所以又写了另外的函数string FloatLength ( float fNum ) 这个统计起来就没问题了..
    */

      下面的是他的代码:

    int i = 0xbd600d1b;
    float *f = (float *)&i;
    printf("%f ",*f);
    float ff = -0.0547;
    int *ii = (int *)&ff;
    printf("%X ",*ii);

    反正也对吧...但是总有一种无言以对的感觉...

    接下来就要进入正题,讲一讲浮点数在内存中的表示了.

    首先是在c和c#中的浮点类型分两种 单精度float和双精度double. float或者double在内存中的组织都遵循IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53

    然后呢对于一个存储在内存的浮点数都包括3部分 :   符号位  指数位 小数部分  (而float和double的区别就是各部分占得位数可能不同)

         

    好了,既然知道了浮点数在内存中的表示方法,那么来个有意思的东西8.25 用10进制表示是82.5 * 10e-1, 那么用2进制表示是多少呢?

    刚开始的时候我真的蒙了,真心不知道怎么用二进制表示啊(好吧8我是知道怎么用二进制表示的,只要大于0的数我都能用二进制表示出来,但是小数就呵呵了,从来没用到过) 答案是1000.01   琢磨了十分钟才搞明白怎么解这个东西:

    8--->1000      0.25-->0[0*20]  .   0 [0*2-1]     1[1*2-2]              所以就是1000.01了  再转换一下100.001 * 2在转换就成了 1.00001 * 23

    在二进制中010 跟10是等价的.所以啊最后如果用科学计数法表示的话都可以表示成1.xxx * 2xxx的形式  (注意跟十进制的区别80 你可以表示成8*101 但是二进制中不可能出现8这个数字,它只有0和1)

    那么好啊,既然所有的二进制都可以表示成1.xxx * 2xxx那么小数点前面的肯定都是1了所以在存储到内存的时候默认都是1.xxx就好了,也没必要在内存中存储1这个bit了所以23bit的尾数部分,可以表示的精度却变成了24bit,道理就是在这里,那24bit能精确到小数点后几位呢,我们知道9的二进制表示为1001,所以4bit能精确十进制中的1位小数点(意思是要表示0-9必须最少要用4bit.),24bit就能使float能精确到小数点后6位(24/4=6),而对于指数部分,因为指数可正可负,8位的指数位能表示的指数范围就应该为:-127-128了,所以指数部分的存储采用移位存储,存储的数据为元数据+127,下面就看看8.25和120.5在内存中真正的存储方式。

    好吧关于为什么指数要+127的问题,我也不知道.从网上找了段讲解粘贴在下方了,有机会再搞清楚吧:

    书上说之所以要将指数加上 127 来得到阶码,是为了简化浮点数的比较运算,这一点我没有体会出来。但是通过 127 这个偏移量 (移码),可以区分出指数的正负。阶码为 127 时表示指数为 0;阶码小于 127 时表示负指数;阶码大于 127 时表示正指数。

    那么继续看8.25和120.5的内存表示方法   8.25   == 1.0001 * 23

                             

                                 

    恩.以上基本就可以把float和double的东西搞得差不多了..

    大牛还弄了个例子来说明把一个float赋值给double的话 double的值未必等于float..我们来看看吧

     猜测一下下面的程序的输出结果是什么:

    1             float f = 2.2f;
    2             double d = (double)f;
    3             Console.WriteLine(d.ToString("0.0000000000000"));
    4             f = 2.25f;
    5             d = (double)f;
    6             Console.WriteLine(d.ToString("0.0000000000000"));        

    可能输出的结果让大家疑惑不解,单精度的2.2转换为双精度后,精确到小数点后13位后变为了2.2000000476837,而单精度的2.25转换为双精度后,变为了2.2500000000000,为何2.2在转换后的数值更改了而2.25却没有更改呢?很奇怪吧?

    首先我们看看2.25的单精度存储方式,很简单 0 1000 0001 001 0000 0000 0000 0000 0000,而2.25的双精度表示为:0 100 0000 0001 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,这样2.25在进行强制转换的时候,数值是不会变的,

    ps: 先插入一个怎么把十进制小数转换成二进制的小数的方法---->

    而我们再看看2.2呢,2.2用科学计数法表示应该为:将十进制的小数转换为二进制的小数的方法为将小数*2,取整数部分,所以0.282=0.4,所以二进制小数第一位为0.4的整数部分0,0.4×2=0.8,第二位为0,0.8*2=1.6,第三位为1,0.6×2 = 1.2,第四位为1,0.2*2=0.4,第五位为0,这样永远也不可能乘到=1.0,得到的二进制是一个无限循环的排列 00110011001100110011... ,对于单精度数据来说,尾数只能表示24bit的精度,所以2.2的float存储为: 2.2 == 10.00110011.... == 1.000110011... * 21 .所以指数是127+1=128  

    但是这样存储方式,换算成十进制的值,却不会是2.2的,应为十进制在转换为二进制的时候可能会不准确,如2.2,而double类型的数据也存在同样的问题,所以在浮点数表示中会产生些许的误差,在单精度转换为双精度的时候,也会存在误差的问题,对于能够用二进制表示的十进制数据,如2.25,这个误差就会不存在,所以会出现上面比较奇怪的输出结果。

    恩,学习完了.附上原作者的链接  http://www.cnblogs.com/jillzhang/archive/2007/06/24/793901.html

  • 相关阅读:
    ida动态调试--反反调试
    python读取配置文件
    问题解决:局域网内,为啥别人ping不到我的IP
    完全卸载MySQL
    JDK环境配置
    win10无法运行Vmware,怎么办
    查看ie版本
    公开的免费WebService接口分享,用于做接口练习
    SVN更改地址
    Loadrunner录制步骤及说明
  • 原文地址:https://www.cnblogs.com/silentNight/p/5274714.html
Copyright © 2020-2023  润新知