zoukankan      html  css  js  c++  java
  • Float在内存中的存储方式及IEC61131处理

    Float在内存中的存储方式及IEC61131处理

    1,fp3232bits float)类型数据在存储器中占用4Bytes存储,且遵循IEEE-754标准:

    一个浮点数分三部分组成:

    符号位s(1bit: 31b)+指数e(8bits: 30-23b)+底数m(23bits: 22-0b)

    2,符号位s

    Bit31表示符号位,符号位指数值的正负,0表示正数,1表示负数。

    3,指数e

    bit30-238bits表示一个有符号的指数,他是十进制指数加上127所得的 数值。

    所以我们计算指数的时候必须减去127

    4,底数m

    Bit22-023bits表示实际存储的底数。

    底数实际占用的是24bits的数据位,由于最高位始终为1,所以不显式显示。

    5,举例

    0.1

    转换为二进制=0.00001

    科学计数表示=1.0*2^-1

    S=0

    E=-1+127=126=0111 1110b

    M=000 0000 _ 0000 0000 0000 0000

     0011 1111 0000 0000 _ 0000 0000 0000 0000

    1.0

    转换为二进制=1.0

    科学计数表示=1.0*2^0

    S=0

    E=0+127=127=0111 1111b

    M=000 0000 _ 0000 0000 0000 0000 

    0011 1111 1000 0000 _ 0000 0000 0000 0000

    3.0

    转换为二进制=11.0

    科学计数表示=1.1*2^1

    S=0

    E=1+127=128=1000 0000b

    M=100 0000 _ 0000 0000 0000 0000 

    0100 0000 0100 0000 _ 0000 0000 0000 0000

    10.0

    转换为二进制=1010.0

    科学计数表示=1.01*2^3

    S=0

    E=3+127=130=1000 0010b

    M=010 0000 _ 0000 0000 0000 0000 

    0100 0001 0010 0000 _ 0000 0000 0000 0000

    -10.0

    转换为二进制=-1010.0

    科学计数表示=-1.01*2^3

    S=1

    E=3+127=130=1000 0010b

    M=010 0000 _ 0000 0000 0000 0000 

    1100 0001 0010 0000 _ 0000 0000 0000 0000

    1.625:

    转换为二进制=1.101

    科学计数表示=1.101*2^0

    S=0

    E=0+127=127=0x7F=0111 1111b

    M=101 0000_0000 0000 0000 0000 

    0011 1111 1101 0000_0000 0000 0000 0000

     

    实际代码操作中,我们对fp32进行手动编码操作的过程如下:

    • 考虑到0的特殊性,对0作单独处理。
    • 非零值,底数均要转化为1.x * 2 ^e
    • 定义指数e=0;先把原始数据转码:

    大于2的除2,直到小于2为止,每次操作均要e++

    小于1的乘2,直到大于1为止,每次操作均要e--

    • 此时我们已经取到了指数ee+127就是我们指数域的数值了。

    下面是取指的操作过程:

     1 (*    小于1不为0的数据, 直接放大到>1        *)
     2 WHILE (real_data < 1.0) AND (real_data <> 0.0) DO
     3     real_data := real_data * 2.0;
     4     temp_E := temp_E - 1;
     5 END_WHILE;
     6 (*    大于2的数据, 直接缩小至<2                *)
     7 WHILE (real_data > 2.0) DO
     8     real_data := real_data / 2.0;
     9     temp_E := temp_E + 1;
    10 END_WHILE;

    temp_E就是我们得到的实际指数。 

    • 此时的底数为1.x,由于1固定不写,所以我们把小数部分0.x转化为二进制数值,左对齐,写入到码值的22..0数据域即可。
    • 循环体以小数码值不为0 与 数据域23bits写满作为条件。
    • 小数部分每次乘2,记录整数部分是否为1,为1的话当前对应的bit1
    • 更新小数部分的值,更新当前的置位位置。
    • 取整数的过程,需要考虑不同平台的差异性,避免由于四舍五入出错。

     下面是我用IEE61131 ST编写的取底数操作:

     

     1 (* 5, 计算底数, bit22..00                    *)
     2 int_pos := 22;                                            (* 第一个小数位的起始点    *)
     3 IF real_data <> 0.0 THEN
     4     real_dec := real_data - 1.0;
     5 ELSE
     6     real_dec := 0.0;
     7 END_IF;
     8 
     9 WHILE (real_dec > 0.0) AND (int_pos > -1) DO
    10     real_tmp := real_dec * 2.0;
    11     (* 判断是否是1                            *)
    12     real_int := REAL_TO_UDINT(real_tmp - 0.5);
    13     IF real_int > UDINT#0 THEN
    14         real_dec := real_tmp - 1.0;
    15         temp_M   := temp_M + SHL_DWORD(DWORD#1, int_pos);
    16     ELSE
    17         real_dec := real_tmp;
    18     END_IF;
    19     int_pos := int_pos - 1;
    20 END_WHILE;

     

    temp_M就是最后计算的底数码值。

    6,解码计算浮点过程是对上述操作的反向定义,相对比较容易写。

    7,我们可以分指数、整数和小数分别解析。

    8,指数直接取出即可

    1 (* 2, 解析指数: bit30..23            *)
    2 t_E := DWORD_TO_INT(SHR_DWORD(UDINT_TO_DWORD(code32), 23) AND DWORD#16#FF);
    3 IF t_E <> 0 THEN
    4     t_E := t_E - 127;
    5 END_IF;

    9,整数部分

    指数为正的乘2累加,比如:1010

    1 -> 1

    0 -> 1*2+0=2

    1 -> 2*2+1=5

    0 -> 5*2+0=10

     

    指数为负的除2累加,结果累加到小数。

     1 (* 4, 计算整数部分数据                *)
     2 bTemp := t_E;
     3 IF bTemp < INT#0 THEN
     4     t_Int := UDINT#0;
     5     t_Dec := 1.0;
     6     WHILE bTemp < 0 DO
     7         bTemp := bTemp + 1;
     8         t_M   := t_M / UDINT#2;
     9         t_Dec := t_Dec / 2.0;
    10     END_WHILE;
    11 ELSE
    12     t_Int := UDINT#1;
    13     t_Dec := 0.0;
    14     WHILE bTemp > 0 DO
    15         t_Int := t_Int * UDINT#2;
    16         IF (UDINT_TO_DWORD(t_M) AND DWORD#16#80000000) <> DWORD#0 THEN
    17             t_Int := t_Int + UDINT#1;
    18         END_IF;
    19         bTemp := bTemp - 1;
    20         t_M := t_M * UDINT#2;
    21     END_WHILE;
    22 END_IF;

    10,小数部分除2累加,比如:101

    1 -> 1/2=0.5

    0 -> 0.5+0=0.5

    1 -> 0.5+1/8=0.625

    1 (* 4, 计算小数部分数据                *)
    2 factor := 2.0;
    3 WHILE t_M > UDINT#0 DO
    4     IF (UDINT_TO_DWORD(t_M) AND DWORD#16#80000000) <> DWORD#0 THEN
    5         t_Dec := t_Dec + 1.0 / factor;
    6     END_IF;
    7     factor := factor * 2.0;
    8     t_M := t_M * UDINT#2;
    9 END_WHILE;

    11,最后根据符号位调整数值结果即可。

    12,同时要注意的是码值为0的时候直接浮点为0.0即可。

    博客园:http://www.cnblogs.com/linux-farmer/
  • 相关阅读:
    ZOJ 1002 Fire Net (火力网)
    UVa OJ 117 The Postal Worker Rings Once (让邮差只走一圈)
    UVa OJ 118 Mutant Flatworld Explorers (变体扁平世界探索器)
    UVa OJ 103 Stacking Boxes (嵌套盒子)
    UVa OJ 110 MetaLoopless Sorts (无循环元排序)
    第一次遇到使用NSNull的场景
    NSURL使用浅析
    从CNTV下载《小小智慧树》
    NSDictionary and NSMutableDictionary
    Category in static library
  • 原文地址:https://www.cnblogs.com/linux-farmer/p/11921275.html
Copyright © 2011-2022 走看看