美文网首页
浮点数的表示

浮点数的表示

作者: 达微 | 来源:发表于2021-10-20 07:40 被阅读0次

语言和C#语言中,对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit,double数据占用64bit,我们在声明一个变量float f= 2.25f的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是float还是double在存储方式上都是遵从IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53。

无论是单精度还是双精度在存储中都分为三个部分:

·符号位(Sign) : 0代表正,1代表为负

·指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储

·尾数部分(Mantissa):尾数部分

其中float的存储方式如下图所示:

4b06d956e5632640815c9b6e367707cf.png

而双精度的存储方式为:

453886849cad01b1b267ba91486d0d2c.png

R32.24和R64.53的存储方式都是用科学计数法来存储数据的,比如8.25用十进制的科学计数法表示就为:8.25* ,而120.5可以表示为:1.205* ,这些小学的知识就不用多说了吧。而我们傻蛋计算机根本不认识十进制的数据,他只认识0,1,所以在计算机存储中,首先要将上面的数更改为二进制的科学计数法表示,8.25用二进制表示可表示为1000.01,我靠,不会连这都不会转换吧?那我估计要没辙了。120.5用二进制表示为:1110110.1用二进制的科学计数法表示1000.01可以表示为1.00012^3 ,1110110.1可以表示为1.11011012^6 ,任何一个数都的科学计数法表示都为1.xxx*

,尾数部分就可以表示为xxxx,第一位都是1嘛,干嘛还要表示呀?可以将小数点前面的1省略,所以23bit的尾数部分,可以表示的精度却变成了24bit,道理就是在这里,那24bit能精确到小数点后几位呢,我们知道9的二进制表示为1001,所以4bit能精确十进制中的1位小数点,24bit就能使float能精确到小数点后6位,而对于指数部分,因为指数可正可负,8位的指数位能表示的指数范围就应该为:-127-128了,所以指数部分的存储采用移位存储,存储的数据为元数据+127,下面就看看8.25和120.5在内存中真正的存储方式。

小数点右移指数为正,小数点左移指数为负数

首先看下8.25,用二进制的科学计数法表示为:1.0001*

2^3

按照上面的存储方式,符号位为:0,表示为正,指数位为:3+127=130 ,位数部分为,故8.25的存储方式如下图所示:

824c87887e643358303481c357adffb6.png

而单精度浮点数120.5的存储方式如下图所示:

677e3f0013b9e53e2889678b5d31e942.png

那么如果给出内存中一段数据,并且告诉你是单精度存储的话,你如何知道该数据的十进制数值呢?其实就是对上面的反推过程,比如给出如下内存数据:0100001011101101000000000000,首先我们现将该数据分段,0 10000 0101 110 1101 0000 0000 0000 0000,在内存中的存储就为下图所示:

c3cf47dd34b7ca85a60922544aa94d1f.png

根据我们的计算方式,可以计算出,这样一组数据表示为:1.1101101*2^6

=120.5

而双精度浮点数的存储和单精度的存储大同小异,不同的是指数部分和尾数部分的位数。所以这里不再详细的介绍双精度的存储方式了,只将120.5的最后存储方式图给出,大家可以仔细想想为何是这样子的

ae6994bed228d3888c87fb49899195a6.png

下面我就这个基础知识点来解决一个我们的一个疑惑,请看下面一段程序,注意观察输出结果

        float f = 2.2f;

        double d = (double)f;

        Console.WriteLine(d.ToString("0.0000000000000"));

        f = 2.25f;

        d = (double)f;

        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在进行强制转换的时候,数值是不会变的,而我们再看看2.2呢,2.2用科学计数法表示应该为:将十进制的小数转换为二进制的小数的方法为将小数2,取整数部分,所以0.282=0.4,所以二进制小数第一位为0.4的整数部分0,0.4×2=0.8,第二位为0,0.82=1.6,第三位为1,0.6×2 = 1.2,第四位为1,0.2*2=0.4,第五位为0,这样永远也不可能乘到=1.0,得到的二进制是一个无限循环的排列 00110011001100110011... ,对于单精度数据来说,尾数只能表示24bit的精度,所以2.2的float存储为:

088518dcf1731d435a20dbcfd8d1869d.png

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

注:本文在写作过程中,参照了如下资料:

http://www.msdn.net/library/chs/default.asp?url=/library/CHS/vccore/html/_core_why_floating_point_numbers_may_lose_precision.asp

http://blog.csdn.net/ganxingming/archive/2006/12/19/1449526.aspx
https://blog.csdn.net/monokai/article/details/108153010

==================float 二进值表示===========================
32.35           1000010000000010110011001100110,
32移码 = 5+127  10000100
-32.35          11000010000000010110011001100110,
32移码 = 5+127  10000100
31.35           1000001111110101100110011001101,
31移码 = 4+127  10000011
16.35           1000001100000101100110011001101,
16移码 = 4+127  10000011
8.35            1000001000001011001100110011010,
8移码 = 3+127   10000010
4.35            1000000100010110011001100110011,
4移码 = 2+127   10000000
2.35            1000000000101100110011001100110,
2移码 = 1+127   10000000
1.35            111111101011001100110011001101,
1移码 = 0+127   1111111
0.35            111110101100110011001100110011,
0移码= -2+127    1111101
-0.35           10111110101100110011001100110011,
0移码= -2+127   1111101
==================double 二进值表示===========================
32.35            100000001000000001011001100110011001100110011001100110011001101,
32移码 = 5+1023  10000000100
-32.35           1100000001000000001011001100110011001100110011001100110011001101,
32移码 = 5+1023  10000000100
16.35            100000000110000010110011001100110011001100110011001100110011010,
16移码 = 4+1023  10000000011
8.35             100000000100000101100110011001100110011001100110011001100110011,
8移码 = 3+1023   10000000010
4.35             100000000010001011001100110011001100110011001100110011001100110,
4移码 = 2+1023   10000000001
2.35             100000000000010110011001100110011001100110011001100110011001101,
2移码 = 1+1023   10000000000
1.35             11111111110101100110011001100110011001100110011001100110011010,
1移码 = 0+1023   1111111111
0.35 = -2+1023  11111111010110011001100110011001100110011001100110011001100110,
0 移码           1111111101
-0.35 = -2+1023  1011111111010110011001100110011001100110011001100110011001100110,
0 移码           1111111101


public class FloatAndDoubleTest
{
    public static void main(String[] args) {
        System.out.println("==================float 二进值表示===========================");
        System.out.println(String.format(
                "32.35           %s,\n" +
                "32移码 = 5+127  %s" ,Integer.toBinaryString(Float.floatToIntBits(32.35f)),Integer.toBinaryString(5+127)));
        System.out.println(String.format(
                "-32.35          %s,\n" +
                "32移码 = 5+127  %s" ,Integer.toBinaryString(Float.floatToIntBits(-32.35f)),Integer.toBinaryString(5+127)));
        System.out.println(String.format(
                "31.35           %s,\n" +
                 "31移码 = 4+127  %s" ,Integer.toBinaryString(Float.floatToIntBits(31.35f)),Integer.toBinaryString(4+127)));
        System.out.println(String.format(
                "16.35           %s,\n" +
                "16移码 = 4+127  %s" ,Integer.toBinaryString(Float.floatToIntBits(16.35f)),Integer.toBinaryString(4+127)));
        System.out.println(String.format(
                "8.35            %s,\n" +
                "8移码 = 3+127   %s" ,Integer.toBinaryString(Float.floatToIntBits(8.35f)),Integer.toBinaryString(3+127)));
        System.out.println(String.format(
                "4.35            %s,\n" +
                "4移码 = 2+127   %s" ,Integer.toBinaryString(Float.floatToIntBits(4.35f)),Integer.toBinaryString(2+126)));
        System.out.println(String.format(
                "2.35            %s,\n" +
                "2移码 = 1+127   %s" ,Integer.toBinaryString(Float.floatToIntBits(2.35f)),Integer.toBinaryString(1+127)));
        System.out.println(String.format(
                "1.35            %s,\n" +
                "1移码 = 0+127   %s" ,Integer.toBinaryString(Float.floatToIntBits(1.35f)),Integer.toBinaryString(127)));
        System.out.println(String.format(
                "0.35            %s,\n" +
                "0移码= -2+127    %s" ,Integer.toBinaryString(Float.floatToIntBits(0.35f)),Integer.toBinaryString(125) ));
        System.out.println(String.format(
                "-0.35           %s,\n" +
                "0移码= -2+127   %s" ,Integer.toBinaryString(Float.floatToIntBits(-0.35f)),Integer.toBinaryString(125)));

        System.out.println("==================double 二进值表示===========================");
        System.out.println(String.format(
                "32.35            %s,\n" +
                "32移码 = 5+1023  %s" ,Long.toBinaryString(Double.doubleToLongBits(32.35d)),Long.toBinaryString(5+1023)));
        System.out.println(String.format(
                "-32.35           %s,\n" +
                "32移码 = 5+1023  %s" ,Long.toBinaryString(Double.doubleToLongBits(-32.35d)),Long.toBinaryString(5+1023)));
        System.out.println(String.format(
                "16.35            %s,\n" +
                "16移码 = 4+1023  %s" ,Long.toBinaryString(Double.doubleToLongBits(16.35d)),Long.toBinaryString(4+1023)));
        System.out.println(String.format(
                "8.35             %s,\n" +
                "8移码 = 3+1023   %s" ,Long.toBinaryString(Double.doubleToLongBits(8.35d)),Long.toBinaryString(3+1023)));
        System.out.println(String.format(
                "4.35             %s,\n" +
                "4移码 = 2+1023   %s" ,Long.toBinaryString(Double.doubleToLongBits(4.35d)),Long.toBinaryString(2+1023)));
        System.out.println(String.format(
                "2.35             %s,\n" +
                "2移码 = 1+1023   %s" ,Long.toBinaryString(Double.doubleToLongBits(2.35d)),Long.toBinaryString(1+1023)));
        System.out.println(String.format(
                "1.35             %s,\n" +
                "1移码 = 0+1023   %s" ,Long.toBinaryString(Double.doubleToLongBits(1.35d)),Long.toBinaryString(1023)));
        System.out.println(String.format(
                "0.35 = -2+1023  %s,\n" +
                "0 移码           %s" ,Long.toBinaryString(Double.doubleToLongBits(0.35d)),Long.toBinaryString(1021)));
        System.out.println(String.format(
                "-0.35 = -2+1023  %s,\n" +
                "0 移码           %s" ,Long.toBinaryString(Double.doubleToLongBits(-0.35d)),Long.toBinaryString(1021)));


    }
}

32.35 = 1.xxxx *2^5

相关文章

  • PHP浮点数精度问题

    PHP常见的浮点数“bug” 浮点数的表示形式 浮点数的表示(IEEE 754): 浮点数, 以64位的长度(双精...

  • 1.2 浮点型运算

    在计算机中,浮点数虽然表示的范围大,但是,浮点数有个非常重要的特点,就是浮点数常常无法精确表示。浮点数在内存的表示...

  • 数据类型操作( 其他)

    浮点数 Python 中使用浮点数来表示不是整数的数字。 浮点数表示的数字的例子:0.5 和 -7.8237591...

  • 格式化输出

    1、三种占位符: %s:可表示字符串,整数,浮点数 %d:可表示整数,浮点数保留整数部分 %f:可表示整数(浮点数...

  • 计算机组成与体系结构知识点一

    数据的表示1、如果浮点数的阶码用R位的移码表示,尾数用M位的补码表示,则这种浮点数的数值范围为: 2、浮点数的 决...

  • 第一张出题 1

    下列关于浮点数与定点小数的利与弊说法错误的是__ A.浮点数表示数的范围比定点数表示数的范围小 .B.浮点数表示数...

  • Python基础知识点拾遗

    浮点数 系统浮点数信息 1.完整信息 显示结果为: 2.浮点数能表示的最大值 3.浮点数能表示的最近接0的值 4....

  • 一.计算机系统构成及硬件基础知识--浮点数的表示

    浮点数的表示方法:

  • 浮点数的表示

    由于机器字长有限,定点数不能表示很大或者很小的实数,直接导致产生了实数近似值的表示方法——浮点数。 value =...

  • 浮点数的表示

    语言和C#语言中,对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据...

网友评论

      本文标题:浮点数的表示

      本文链接:https://www.haomeiwen.com/subject/pgpaoltx.html