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/
  • 相关阅读:
    python数据结构之树(二叉树的遍历)
    python数据结构之树(概述)
    python面向对象高级:定制类
    python面向对象高级:Mixin多重继承
    frp
    CentOS7 安装远程桌面
    Java-JVM 锁优化
    Java-内存模型 synchronized 的内存语义
    Java-内存模型 final 和 volatile 的内存语义
    Java-内存模型(JSR-133)
  • 原文地址:https://www.cnblogs.com/linux-farmer/p/11921275.html
Copyright © 2011-2022 走看看