精华内容
下载资源
问答
  • 二进制浮点数计算器
    千次阅读
    2021-05-24 03:33:22

    使用十进制浮点数,可以避免二进制浮点数与我们习惯的十进制数之间的表示误差.这个在金融领域是非常重要的.但是计算机基本都只能对二进制浮点数进行计算,也就是IEEE754格式表示的浮点数.很多程序都会自己模拟十进制浮点数的计算.为了统一,IEEE754做了扩展,包括了十进制的浮点数.

    IEEE 754-2008里面规定了十进制浮点数的一些规范.不过里面没有说具体的二进制表示方法.只是规定了32位,64位,128位的十进制浮点数的表示范围和有效位数. 因为具体一个浮点数的二进制里面每个位表示啥,都是每个机器自己决定的.不需要跟外界一致.只是在传输的时候要保证数据的精度和范围一致就行了.下表来自wikipedia,列出了每种浮点数的有效位数,指数的范围.

    Name

    Common name

    Base

    Digits

    E min

    E max

    binary16

    Half precision

    2

    10+1

    -14

    15

    binary32

    Single precision

    2

    23+1

    -126

    127

    binary64

    Double precision

    2

    52+1

    -1022

    1023

    binary128

    Quadruple precision

    2

    112+1

    -16382

    16383

    decimal32

    10

    7

    -95

    96

    decimal64

    10

    16

    -383

    384

    decimal128

    10

    34

    -6143

    6144

    实际的系统中,十进制浮点数有两种表示方法,分别是Densely Packed Decimal(密集十进制数)和Binary Integer Decimal(二进制整数表示的十进制数).

    DPD表示方便转换成十进制的浮点数字符串,但是需要专门的计算单元来做计算,软件模拟比较麻烦.

    而BID表示更直观,转换到二进制会比较容易.很方便用二进制的整数运算单元来计算.

    所以Power6上有了硬件的十进制浮点计算单元,就用DPD表示.而在x86 x64 cpu上没有十进制计算单元, 各种软件实现的十进制浮点库默认大都用BID方式表示.比如Intel就实现了一个开源的c 语言的十进制浮点数库。

    十进制浮点的意义,在于更符合人们的习惯,比如下面的例子

    #include

    <

    stdio

    .

    h

    >

    int

    main

    (

    )

    {

    double a

    =

    7

    .

    ;

    double b

    =

    0

    .

    00007

    ;

    printf

    (

    "%d/n"

    ,

    a

    =

    =

    b

    *

    100000

    )

    ;

    }

    正确的输出应该是1,但是实际的输出结果是0,在做相等比较的时候,还不得不考虑一下这个误差了。而某些时候误差会在计算过程中累计,变成比较明显的错误了。

    如果用intel的十进制浮点库赖做这个计算,结果就会不同了。intel这个库明显还在试验阶段,用起来比较麻烦。

    int

    main

    (

    )

    {

    Decimal64 a

    ,

    b

    ,

    c

    ;

    _IDEC_round my_rnd_mode

    =

    _IDEC_dflround

    ;

    _IDEC_flags my_fpsf

    =

    _IDEC_allflagsclear

    ;

    a

    =

    bid64_from_int32

    (

    7

    )

    ;

    b

    =

    bid64_from_string

    (

    "0.00007"

    ,

    my_rnd_mode

    ,

    &

    my_fpsf

    )

    ;

    c

    =

    bid64_mul

    (

    b

    ,

    bid64_from_int32

    (

    100000

    )

    ,

    my_rnd_mode

    ,

    &

    my_fpsf

    )

    ;

    printf

    (

    "%d/n"

    ,

    bid64_quiet_equal

    (

    a

    ,

    c

    ,

    &

    my_fpsf

    )

    )

    ;

    return 0

    ;

    }

    使用和double位数相同的Decimal64,结果就是1了。这里显然不是精度的问题,而是十进制浮点数能丝毫不变的表示十进制的小数。

    我们可以看到这里使用的是BID的表示方法。函数名前面都带个bid前缀。

    接下来,我们来具体看看BID的表示方法,我们可以把刚才程序中的a和c按照十六进制输出

    printf("%llx/n%llx/n",a,c);

    结果是

    31c0000000000007

    31200000000aae60

    可见,两个相等的十进制浮点数的BID表示不一定是相同的。也就是说,一个数有多种表示方法。

    a的表示里,最低位的16进制数就是7,而c的表示里,最低的5位15进制数aae60,其实就是十进制的700000。看来这后面的就是有效数字部分了。查一下BID的表示方法,还是比较复杂的,有6种情况。最高位是符号位,这里当然是0.符号位后面的两位是00,01,或10时,64位BID每个位的意义是这样的,s后面的2位和之后的8位是指数部分,之后53位T和t都是有效数字部分

    s 00eeeeeeee TTTtttttttttttttttttttt tttttttttttttttttttttttttttttt

    s 01eeeeeeee TTTtttttttttttttttttttt tttttttttttttttttttttttttttttt

    s 10eeeeeeee TTTtttttttttttttttttttt tttttttttttttttttttttttttttttt

    而如果符号位后面的两位是11,那么每一位的意义是

    s 11 00eeeeeeee (100)Ttttttttttttttttttttt tttttttttttttttttttttttttttttt

    s 11 01eeeeeeee (100)Ttttttttttttttttttttt tttttttttttttttttttttttttttttt

    s 11 10eeeeeeee (100)Ttttttttttttttttttttt tttttttttttttttttttttttttttttt

    这时,有效数字前面就加了隐含的100.

    这个BID表示的数的值就是 (-1)^S *T*10^(E-398) ,其中T 是实际的有效数字(就是说如果有隐含的100需要加上后计算),E是指数,T,E都是2进制表示的

    还是回到我们的例子

    a的二进制数

    0 0110001110 00000 00000000 00000000 00000000 00000000 00000000 00000111

    指数部分就是0110001110,也就是398,所以a就是 7*10^(398-398) ,也就是7

    而c的二进制是

    0 0110001001 00000 00000000 00000000 00000000 00001010 10101110 01100000

    指数部分是 0110001001,也就是393, 所以c的值是 700000*10^(393-398), 还是7.

    这就能看明白为啥同样是7,二进制表示却不同。这也是十进制浮点和二进制浮点一个不同之处,十进制浮点没有规定一定要是哪一种表示。这也给相等比较带来了一点麻烦。

    power6 里面内置了十进制浮点计算单元,而power6上面的编译器也就支持了内置的十进制浮点类型。前面已经说了,power上面的十进制浮点才用的是DPD表示方法。还是看个程序吧。下面这个程序在一个使用Power6的P520机器上,操作系统是AIX5.3 ML6, 用xlc 10.2编译。_Decimal64就是64位的十进制浮点。

    int

    main

    (

    int

    argc

    ,

    char

    *

    *

    argv

    )

    {

    long i

    ,

    count

    ;

    double dfund

    ,

    dinterest

    ;

    _Decimal64 Dfund

    ,

    Dinterest

    ;

    /

    *

    定义十进制浮点类型的变量

    *

    /

    long long value

    ;

    union trans

    {

    _Decimal64 dv

    ;

    int

    av

    [

    2

    ]

    ;

    }

    transTemp

    ;

    dfund

    =

    atof

    (

    argv

    [

    1

    ]

    )

    ;

    dinterest

    =

    atof

    (

    argv

    [

    2

    ]

    )

    ;

    Dfund

    =

    atodecimal

    (

    argv

    [

    1

    ]

    )

    ;

    Dinterest

    =

    atodecimal

    (

    argv

    [

    2

    ]

    )

    ;

    count

    =

    atoi

    (

    argv

    [

    3

    ]

    )

    ;

    /

    *

    下面把_Decimal64 类型的Dinterest转换成两个int,然后按照十六进制格式显示

    *

    /

    transTemp

    .

    dv

    =

    Dinterest

    ;

    printf

    (

    "value=%#x,%#x/n"

    ,

    transTemp

    .

    av

    [

    ]

    ,

    transTemp

    .

    av

    [

    1

    ]

    )

    ;

    printf

    (

    "double  fund=%20.10f interest=%40.30f/n"

    ,

    dfund

    ,

    dinterest

    )

    ;

    printf

    (

    "Decimal fund=%20.10Df interest=%40.30Df/n"

    ,

    Dfund

    ,

    Dinterest

    )

    ;

    /

    *

    printing them with the new printf specifiers

    *

    /

    for

    (

    i

    =

    ;

    i

    <

    count

    ;

    i

    +

    +

    )

    {

    dfund

    =

    dfund

    *

    dinterest

    ;

    Dfund

    =

    Dfund

    *

    Dinterest

    ;

    /

    *

    performing maths

    *

    /

    }

    printf

    (

    "Print final funds/n"

    )

    ;

    printf

    (

    "double  fund=%30.10f/n"

    ,

    dfund

    )

    ;

    printf

    (

    "Decimal fund=%30.10Df/n"

    ,

    Dfund

    )

    ;

    }

    其中 atodecimal是自己写的一个帮助函数

    _Decimal64 atodecimal

    (

    char

    *

    s

    )

    {

    _Decimal64 top

    =

    ,

    bot

    =

    ,

    result

    ;

    int

    negative

    =

    ,

    i

    ;

    if

    (

    s

    [

    ]

    =

    =

    '

    -

    '

    )

    {

    negative

    =

    1

    ;

    s

    +

    +

    ;

    }

    if

    (

    s

    [

    ]

    =

    =

    '

    +

    '

    )

    s

    +

    +

    ;

    for

    (

    ;

    isdigit

    (

    *

    s

    )

    ;

    s

    +

    +

    )

    {

    top

    =

    top

    *

    10

    ;

    top

    =

    top

    +

    *

    s

    -

    '

    '

    ;

    }

    if

    (

    *

    s

    =

    =

    '

    .

    '

    )

    {

    s

    +

    +

    ;

    for

    (

    i

    =

    strlen

    (

    s

    )

    -

    1

    ;

    isdigit

    (

    s

    [

    i

    ]

    )

    ;

    i

    -

    -

    )

    {

    bot

    =

    bot

    /

    10

    ;

    bot

    =

    bot

    +

    (

    _Decimal64

    )

    (

    s

    [

    i

    ]

    -

    '

    '

    )

    /

    (

    _Decimal64

    )

    10

    ;

    }

    }

    result

    =

    top

    +

    bot

    ;

    if

    (

    negative

    )

    result

    =

    -

    result

    ;

    return result

    ;

    }

    这个程序用xlc 10.2编译时,跟上参数表示使用硬件十进制浮点。不过这样会导致编译出来的可执行文件在power5以前的cpu上无法运行。

    运行的时候输入参数 ./dfp_hw 1 1.00000091 6000000

    dfp_hw是程序的名字,1就是程序里面的 fund,1.00000091是interest,也就是利息,6000000是count,输出结果:

    value=0x22180000,0x800001b

    double  fund=        1.0000000000 interest=        1.000000910000000020616539586630

    Decimal fund=        1.0000000000 interest=        1.000000910000000000000000000000

    Print final funds

    double  fund=                235.0968403429

    Decimal fund=                235.0968403137

    可以看到用double存储利息,再输出,就不再是1.00000091了,后面有一点误差。而用_Decimal64存储输入结果,再输出,是一点误差都没有。

    然后把interest乘6000000次,也就是1.0000091的6000000次方,输出的结果误差就比较明显了。用windows自带的计算器可以验证,_Decimal64的结果是正确的。

    现在来看看1.00000091的二进制表示。也就是0x22180000,0x800001b,注意这里这个power机器是大端的,所以前面以前是高4字节,后面是低4字节。连起来看,就是0x22180000 0800001b也就是

    00100010 00011000 00000000 00000000 00001000 00000000 00000000 00011011

    DPD表示方法也比较复杂,从高位开始看,第一位还是符号位0,DPD的规定如果符号位后面的两位是00,01,或者10,那么每一位的意义如下

    s 00 mmm (00)eeeeeeee (mmm)[tttttttttt][tttttttttt][tttttttttt][tttttttttt][tttttttttt]

    s 01 mmm (01)eeeeeeee (mmm)[tttttttttt][tttttttttt][tttttttttt][tttttttttt][tttttttttt]

    s 10 mmm (10)eeeeeeee (mmm)[tttttttttt][tttttttttt][tttttttttt][tttttttttt][tttttttttt]

    其中,e是指数,e的表示方法跟前面的BID方式很像。t和m是有效数字,其中,每10位t组成一个declet,表示一个3位的十进制数。m实际的位置是在第4位到第6位,但是它逻辑上的位置是在那些t前面,所以用()表示放到e的后面。

    因为2的10次方是1024,刚好能表示10的3次方。但是表示起来还是需要点技巧的,declet表示三位十进制数的规则比较复杂,这也是这个表示方法叫Densely Packed Decimal(密集十进制数)的原因。下表是编码的方式。b9-b0代表10个二进制数,d2 d1 d0代表3个十进制数。

    b9

    b8

    b7

    b6

    b5

    b4

    b3

    b2

    b1

    b0

    d2

    d1

    d0

    编码值

    数位的模式

    a

    b

    c

    d

    e

    f

    g

    h

    i

    0abc

    0def

    0ghi

    (0

    7) (0

    7) (0

    7)

    3

    位小数字

    a

    b

    c

    d

    e

    f

    1

    i

    0abc

    0def

    100i

    (0

    7) (0

    7) (8

    9)

    两位小数字,一位大数字

    a

    b

    c

    d

    e

    f

    1

    1

    i

    0abc

    100f

    0dei

    (0

    7) (8

    9) (0

    7)

    a

    b

    c

    d

    e

    f

    1

    1

    i

    100c

    0def

    0abi

    (8

    9) (0

    7) (0

    7)

    a

    b

    c

    1

    f

    1

    1

    1

    i

    0abc

    100f

    100i

    (0

    7) (8

    9) (8

    9)

    一位小数字,两位大数字

    a

    b

    c

    1

    f

    1

    1

    1

    i

    100c

    0abf

    100i

    (8

    9) (0

    7) (8

    9)

    a

    b

    c

    f

    1

    1

    1

    i

    100c

    100f

    0abi

    (8

    9) (8

    9) (0

    7)

    x

    x

    c

    1

    1

    f

    1

    1

    1

    i

    100c

    100f

    100i

    (8

    9) (8

    9) (8

    9)

    三位大数字

    就我们的例子来看一下,最低的10位是0000011011,看b3b2b1,这里是101,所以就是上表第3行的情况,三位数字就是 (0000)(1001)(0001)也就是091,然后看从低位数的第3个10位二进制数,也就是00100000000,这显然是第一种情况,也就是100,连起来就是100000091,指数部分是390,那么这个十进制的值就是 10^(390-398)*100000091 = 1.00000091.

    通过这个简单的例子,就应该对DPD方式的十进制浮点表示方式有个大概的了解了。这个方式算起来比较麻烦,所以除非有硬件支持,软件模拟的方式都不会使用的,但是DPD转换成十进制浮点的字符串表示就会很方便。

    更多相关内容
  • 好用的浮点数二进制转换工具,通讯开发必备实用小工具,4字节浮点数转换,支持正序倒序转换。浮点数二进制二进制浮点数,转换准确。
  • 二进制浮点数的加减法运算

    千次阅读 多人点赞 2019-09-27 11:45:55
    二进制浮点数的规格化表示形式定义为N=2E⋅MN=2^E·MN=2E⋅M其中MMM称为尾数,EEE称为阶码 例如二进制浮点数11.011011的规格化表示形式为:2+0010×0.110110112^{+0010}×0.110110112+0010×0.11011011该浮点数在...

    二进制浮点数的规格化表示形式定义为 N = 2 E ⋅ M N=2^E·M N=2EM其中 M M M称为尾数, E E E称为阶码

    例如二进制浮点数11.011011的规格化表示形式为: 2 + 0010 × 0.11011011 2^{+0010}×0.11011011 2+0010×0.11011011该浮点数在计算机中存储为:

    00100011011011
    
    各个二进制位代表的含义为:
    阶符E | 阶码E | 尾符M | 尾码M
    00      010    00      11011011
    

    现假设有两浮点数 X X X Y Y Y
    X : 2 + 010 × 0.11011011 X:2^{+010}×0.11011011 X:2+010×0.11011011 Y : 2 + 100 × − 0.10101100 Y:2^{+100}×-0.10101100 Y2+100×0.10101100
    X X X Y Y Y在计算机中表示为:

        阶符E | 阶码E | 尾符M | 尾码M
    X   00      010    00      11011011
    Y   00      100    11      01010100
    

    要在计算机中实现加减法运算要执行5个步骤:

    1. 对阶

    对阶的目的是为了使两个阶数不同的浮点数变换到为可以直接相加,例如在十进制中 1.2 × 1 0 2 1.2×10^2 1.2×102 2.3 × 1 0 3 2.3×10^3 2.3×103的尾数是不能直接相加的,必须转换成相同的阶数才可以相加
    1.2 × 1 0 2 +   2.3 × 1 0 3 ‾ 3.3 × 1 0 ? ⟹ 0.12 × 1 0 3 +   2.3    × 1 0 3 ‾ 2.42 × 1 0 3 \begin{aligned} & \quad 1.2 × 10^2 \\ & \underline{+\ 2.3 × 10^3}\\ & \quad 3.3 × 10^?\\ \end{aligned}\Longrightarrow \begin{aligned} & \quad 0.12 × 10^3 \\ & \underline{+\ 2.3 \ \ × 10^3}\\ & \quad 2.42 × 10^3\\ \end{aligned} 1.2×102+ 2.3×1033.3×10?0.12×103+ 2.3  ×1032.42×103

    1.1 取大阶

    保留两个浮点数中阶码较大的那一个浮点数的阶数,较小阶码的浮点数的阶数在之后需要对齐大阶,取大阶的公式为: E m a x = max ⁡ ( E X , E Y ) E_{max} = \max(E_X,E_Y) Emax=max(EX,EY)之所以是取大阶而不是取小阶是因为,浮点数往小阶对其后,小数点会进入尾数之中,与上面举的例子不同,这种浮点数形式是无法在计算机中存储的。

    取大阶举例,对 X X X Y Y Y取大阶:
    E m a x = max ⁡ ( E X , E Y ) = E Y E_{max} = \max(E_X,E_Y)=E_Y Emax=max(EX,EY)=EY

    1.2 求阶差

    求出阶差后可以根据阶差对阶码小的浮点数的尾数进行调整,求阶差公式: Δ E = ∣ E X − E Y ∣ 补 \Delta E = |E_{X}-E_{Y}|_{补} ΔE=EXEY

    求阶差举例,对 X X X Y Y Y求阶差:
    Δ E = ∣ E X − E Y ∣ 补 = ∣ E X 补 − E Y 补 ∣ = ∣ E X 补 + ( − E Y ) 补 ∣ = ∣ 00010 + 11100 ∣ = ∣ 11110 ∣ = 2 \begin{aligned} \Delta E=& |E_{X}-E_{Y}|_{补}\\ =& |E_{X补}-E_{Y补}|\\ =& |E_{X补}+(-E_{Y})_{补}|\\ =& |00010 + 11100|\\ =& |11110|=2 \end{aligned} ΔE=====EXEYEXEYEX+(EY)00010+1110011110=2

    1.3 对阶

    1.2求阶差 Δ E ≠ 0 \Delta E \neq 0 ΔE=0,则执行对阶操作,将阶码值较小的浮点数尾数右移 Δ E \Delta E ΔE位,使得两个浮点数的阶码值相等。

    由于进行了右移,对阶操作往往会损失阶码值较小的浮点数的一部分精度从而产生计算误差,若要减小计算误差,则要将右移过程中损失的尾数值保留下了以供后面3.2.2左规格化和4.2舍入步骤使用,这里不细说。

    对阶举例,对 X X X Y Y Y对阶:
    已知 X X X Y Y Y的大阶阶码 E m a x = E Y E_{max}=E_Y Emax=EY,阶差 Δ E = 2 \Delta E=2 ΔE=2。即 E X E_X EX位向 E Y E_Y EY对齐,且 M X M_X MX右移 Δ E \Delta E ΔE位:
    00010 ‾ 00 11011011 ‾ ⟹ 00100 ‾ 00 00110110 ‾ \underline{00010}00\underline{11011011}\Longrightarrow\underline{00100}00\underline{00110110} 000100011011011001000000110110对阶后的 X X X Y Y Y:

        阶符E | 阶码E | 尾符M | 尾码M
    X   00      100    00      00110110
    Y   00      100    11      01010100
    

    对阶前的 X X X Y Y Y:

        阶符E | 阶码E | 尾符M | 尾码M
    X   00      010    00      11011011
    Y   00      100    11      01010100
    

    2. 尾数运算

    2.1 尾数运算

    对阶操作完成之后浮点数的尾数就可以进行运算了,公式可以表示为: M = [ ( M X ) ± ( M Y ) ] 补 M=[(M_X)\pm(M_Y)]_补 M=[(MX)±(MY)]

    尾数运算举例,对 X X X Y Y Y的尾数进行运算:
    M = [ ( M X ) + ( M Y ) ] 补 = ( M X ) 补 + ( M Y ) 补 M=[(M_X)+(M_Y)]_补=(M_X)_补+(M_Y)_补 M=[(MX)+(MY)]=(MX)+(MY)
    00100 0000110110 ‾ +   00100 1101010100 ‾ ‾ 00100 1110001010 ‾ \begin{aligned} & \quad 00100\underline{0000110110} \\ & \underline{+\ 00100\underline{1101010100}}\\ & \quad 00100\underline{1110001010}\\ \end{aligned} 001000000110110+ 001001101010100001001110001010

    3. 规格化

    3.1 判断尾数溢出

    双符号位的判断溢出方法:二进制数的符号一定为00(正)或11(负),运算后若符号位变为01则是正溢出,变为10则是负溢出,二进制数相加结果符号位溢出判断如下表所示:

    正数负数正溢负溢
    00110110

    可以简记为,符号位异或的结果为0则溢出,为1则不溢出。

    3.2 左右规格化

    3.2.1 右规格化

    若尾数加减后的结果 M M M溢出则执行右规格化操作,即尾数右移一位且阶码加1,表示为 M ≫ 1 , E + 1 M\gg 1,E+1 M1,E+1

    3.2.2 左规格化

    若尾数加减后的结果 M M M不溢出则执行左规格化操作,即尾数左移K位且阶码减K,表示为 M ≪ K , E − K M\ll K,E-K MK,EK其中 K K K表示将尾数数值最高位变为与尾数符号位不同值需要执行左移的次数。注意此处的左移要包括1.3对阶时被移出低位的尾数值。

    左规格化的目的是为了提高尾数的精度,例如, M = 001001110001010 M=001001110001010 M=001001110001010转为真值表示为 2 4 × − 0.01110110 2^4×-0.01110110 24×0.01110110,由于在计算机中尾数的存储位数有限,这种表示方法就会造成精度的下降,改为 2 3 × − 0.11101100 2^3×-0.11101100 23×0.11101100就可以多存一个位的数据

    尾数运算举例,对 M M M的尾数进行左规格化:
    尾数左移1位(括号中为对阶时被移出低位的尾数值): 1 11 ‾ 0001010 ( 11 ) ⟹ 1 10 ‾ 0010101 ( 1 ) 1\underline{11}0001010(11)\Longrightarrow 1\underline{10}0010101(1) 1110001010(11)1100010101(1)阶码减1: 00100 − 00001 = 00011 00100-00001=00011 0010000001=00011

    4. 舍入

    4.1 判断阶码溢出

    如果在上一步执行的是右规格化,则要判断阶码是否上溢,若执行的是左规格化,则要判断阶码是否下溢。

    判断方法与3.1一致,符号位异或的结果为0则溢出,为1则不溢出。右规格化若上溢则置上溢标志并且报运算结果溢出错误,左规格化若下溢则置机器零到5.4输出结果

    4.2 舍入

    若左右规格化的步骤执行后结果不溢出,则进行舍入处理。

    在执行3.2.1右规格化和1.3对阶时,尾数低位上的数值会被移除掉使得浮点数精度下降。舍入就是用来弥补被移除掉的精度,当移除掉的低位数值最高位为1时,在规格化后的尾数末尾加1,若加1后使得尾数溢出,则需再执行一次右规格化。

    舍入举例,对规格化后的 M M M的尾数进行舍入:
    在1.3对阶时被移除掉的两个低位数是11,则移除掉的低位数值最高位为1,应在规格化后的尾数末尾加1,得到: M = M + 1 = 1100010101 + 000000001 = 1100010110 M=M+1=1100010101+000000001=1100010110 M=M+1=1100010101+000000001=1100010110

    5. 判溢出

    5.1 判断尾数溢出

    执行完舍入之后,要判断舍入之后的尾数是否溢出,判断方法与3.1一致,符号位异或的结果为0则溢出,为1则不溢出。若不溢出则输出结果。

    若溢出则需要在执行一次右规格化,然后判断是否上溢,若上溢则置上溢标志并且报运算结果溢出错误,若不上溢则输出运算结果

    5.4 输出结果

    最终结果可以用 X ± Y X\pm Y X±Y来表示,其值就是EM的真值 X ± Y = E 真 × M 真 X\pm Y=E_真×M_真 X±Y=E×M

    结果举例,对规格化后的 M M M的尾数进行舍入:
    M = 1100010110 ⟹ X + Y = E 真 × M 真 = 2 011 × − 0.11101010 M=1100010110\Longrightarrow X+Y=E_真×M_真=2^{011}×-0.11101010 M=1100010110X+Y=E×M=2011×0.11101010

    展开全文
  • IEEE 754 单精度浮点数转换 在线计算器 http://www.styb.cn/cms/ieee_754.php 十进制小数的二进制表示: 整数部分:除以2,取出余数,商继续除以2,直到得到0为止,将取出的余数逆序 小数部分:乘以2,然后取出整数...

    IEEE 754 单精度浮点数转换 在线计算器

    http://www.styb.cn/cms/ieee_754.php

    十进制小数的二进制表示:

    整数部分:除以2,取出余数,商继续除以2,直到得到0为止,将取出的余数逆序

    小数部分:乘以2,然后取出整数部分,将剩下的小数部分继续乘以2,然后再取整数部分,一直取到小数部分为零为止。如果永远不为零,则按要求保留足够位数的小数,最后一位做0舍1入。将取出的整数顺序排列。

    举例:22.8125 转二进制的计算过程:

    整数部分:除以2,商继续除以2,得到0为止,将余数逆序排列。
    22 / 2 11 余0
    11/2 5 余 1
    5 /2 2 余 1
    2 /2 1 余 0
    1 /2 0 余 1
    得到22的二进制是10110

    小数部分:乘以2,取整,小数部分继续乘以2,取整,得到小数部分0为止,将整数顺序排列。
    0.8125x2=1.625 取整1,小数部分是0.625
    0.625x2=1.25 取整1,小数部分是0.25
    0.25x2=0.5 取整0,小数部分是0.5
    0.5x2=1.0 取整1,小数部分是0,

    得到0.8125的二进制是0.1101

    结果:十进制22.8125等于二进制00010110.1101

    C语言和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的存储方式如下图所示:

    float类型的存储方式

    而双精度的存储方式为:

    double类型数据的存储方式

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

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

    单精度浮点数8.25的存储方式

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

    单精度数120.5的存储方式

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

    根据我们的计算方式,可以计算出,这样一组数据表示为:1.1101101*clip_image002[3]=120.5

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

    文本框: 0     100 0000 0101    1101 1010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

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

            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存储为:

    单精度数202的存储方式

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

    展开全文
  • 浮点数二进制二进制浮点数,E,PI转二进制小工具
  • 在线IEEE浮点二进制计算器工具 在线IEEE浮点二进制计算器工具 这是一个小计算器,旨在帮助您了解用于浮点计算的IEEE 754标准。它是用JavaScript实现的,并且可以与Chrome和Firefox的最新桌面版本一起使用。我尚未在...

    在线IEEE浮点二进制计算器工具

    在线IEEE浮点二进制计算器工具

    这是一个小计算器,旨在帮助您了解用于浮点计算的IEEE 754标准。它是用JavaScript实现的,并且可以与Chrome和Firefox的最新桌面版本一起使用。我尚未在其他浏览器上进行过测试。如果您在左侧的三个框中之一中输入浮点数字,然后按Enter键,您将在右侧看到该数字的位模式。您可以输入使用通常在编程语言接受的语法,比如数字42,2.345,12E-3,等; 您可以输入值NaN,Inf和-Inf直接; 并且您还可以使用语法输入分数17/23。最后,您可以使用如右图所示的输入位模式(以0b或开头的位0x)

    在这里插入图片描述

    https://tooltt.com/ieee/

    展开全文
  • 半精度二进制浮点数的大批量转换,发现没有现成的转换代码,就根据原理用matlab撸了一个,与在线转换器对比暂时没有发现bug,通过写blog的方式debug(小黄鸭调试法),也欢迎大家帮我找茬,一起debug. 半精度浮点数...
  • 浮点型,二进制互相转换工具,可以互相转换!
  • 进制浮点数转化为二进制IEEE单精度浮点数 首先将十进制浮点数转换为定点数,再转化为IEEE单精度浮点数。 例1:将5.25转化为IEEE单精度浮点数 ①将5.25转化为定点数 5——>0101 0.25——>0.01 5.25——>101...
  • 一、十进制浮点数转换为二进制 方法:整数部分除以基数取余,直到商为零 小数部分乘基取整,直到小数部分为零 如:6.625的二进制 整数部分: 6/2=3......0 3/2=1......1 1/2=0......1 ====>110 小数部分...
  • 二进制浮点数向十进制浮点数的转换IEEE给出了一套浮点数值的标准,即IEEE 754。该标准给出了单精度(32位)和双精度(64位)浮点数值的表示方法以及如何对其进行操作。本文简单的给出了从二进制的浮点数到十进制浮点数的...
  • 八进制转二进制计算器八进制数:二进制数:进制:进制:转换说明:上面第一行可将八进制转换为二进制,第二行可以将任意进制转换为任意进制。64个基数为:"[emailprotected]"注意:本工具只是各种进制计数法的数之间...
  • 在线浮点数二进制

    2022-04-28 22:12:20
    浮点数-Float-Double转二进制 浮点数-Float-Double转二进制 浮点数,是属于有理数中某特定子集的数的数字表示,在计算机中用以近似表示任意某个实数。具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数...
  • 十六进制浮点数转化为十进制

    千次阅读 2021-08-25 14:16:19
    这里写自定义目录标题转化公式新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个...* 二进制 11000001011110101010011111101111 * 十进制 -15.666
  • 整数转化为二进制 1.正整数用源码表示 2.负整数用绝对值的补码表示(将绝对值取反+1) 如-50用50的补码表示 50的源码为00000000000000000000000000110010 反码则为11111111111111111111111111001101 补码为反码+...
  • 十六进制转二进制计算器十六进制:二进制:进制:进制:转换说明:上面第一行可将十六进制转换为二进制,第二行可以将任意进制转换为任意进制。64个基数为:"[emailprotected]"注意:本工具只是各种进制计数法的数...
  • 浮点数与十进制数转换工具,可用浮点数转十进制,也可以用十进制浮点数
  • IEEE-754标准(32位) 十六进制转换十进制浮点数

    万次阅读 多人点赞 2020-12-23 00:06:44
    1.将(32位)16进制IEEE-754标准浮点数就是用十六进制表示浮点,称为单精度浮点数。 float类型在内存中占4个字节(32位),最高位用于表示符号;在剩下的31位中,从右向左了8位用于表示指数,其余用于表示尾数。(一...
  • 程序员二进制计算器 v1.36

    热门讨论 2014-07-16 16:21:43
    专为程序员打造的计算器二进制运算强大,支持64位。 采用表达式驱动,输入表达式便即时显示结果,抛弃传统计算器繁琐的按钮,表达式可粘贴或回调重复使用。 支持二进制串直接运算,如0b1101 & 0b0011= 0b0001。 ...
  • 打算用vb做个进制转换计算器!先用c++写了个转换程序,不足之处,多多包涵!
  • 基本功能: 基本四则运算 小数点后至少包含2位 实现任意数平方,开方,三次方。开三次方 实现0到1之间随机数,小数点后至少3位 计算响应时间小于1秒 扩展功能 实现自然对数,和以10为底的对数 三角函数,反三角函数 ...
  • IEEE标准的32位浮点数转换为十进制的计算方法   下面是用笔记本电脑的MODTEST软件在COM 1口经232转485转换器转变为485口后连接到流量计算机,通过MODBUS协议读取到的流量计内部数据,下面通过分析,解释了...
  • C++内存操作十六进制浮点数

    千次阅读 2018-12-27 00:58:36
    问题背景如下,在某些串口通信过程中会得到类似于下图所示的数据,数据格式为每四个十六进制数为一组,表示一个浮点数,在下图所示的数据中每一行包含三个浮点数,最后多出来一个用于校验。其中每个浮点数为左边低...
  • 二进制,十进制,十六进制,单双精度浮点转换器

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,523
精华内容 1,409
关键字:

二进制浮点数计算器