关于64位整数的加减运算

已锁定

水煮花生

  • 帖子

    287
  • 精华

    4
  • 被关注

    8

论坛等级:侠圣

注册时间:2011-04-14

白金 白金 如何晋级?

关于64位整数的加减运算

3351

4

2023-06-11 23:29:58

一些应用场景对64位整数使用比较多,如仪表上位机,累计,多圈绝对编码器,200smart和1200的手册上没有64位整数的类型,对于这些,以前习惯用2^16,2^32结合浮点数变换对付过去。想换种思路,尝试加减法的底层逻辑来处理64位加法和减法,进而还可推演到更多位的加减运算,也算多了一条路,顺便实践一下SCL。

一,加法运算的逻辑,计算a+b,按如下步骤进行:

(1)a  AND b  =c,(与运算,需要进位的标志)。

(2)a XOR b=d,(异或运算,无进位的结果)。

(3)若c为0(即无需进位),d为计算结果,退出到(7);若c不为0(存在进位),c左移1位,得到c。
(4)d AND c =c,(与运算,需要进位的标志)。
(5)d XOR c =d,(异或运算,无进位的结果)。
(6)回到(3),重复。
(7)输出结果  即a+b=d。

以两个32位处理64加法,先按上述思路,计算两个数据低32位的加法, 并保存进位标志;然后计算两个数据高32位的加法,若低32位存在进位标志,高32位加法的结果还要再加1,最终得到64位的加法,分别对应的储存于高低32位地址中。

二,减法运算的逻辑,计算a-b,按如下步骤进行:

(1)a XOR b =c,(异或运算,无进位的结果)。

(2)b AND c=d,(与运算,进位标志)。

(3)若d为0(即无需进位),c为计算结果,退出到(7)  若d不为0(存在进位),d左移1位,得到d。
(4)c XOR d =c,(异或运算,无进位的结果)。

(5)c AND d =d,(与运算,需要进位的标志)。

(6)回到(3),重复。
(7)输出结果  即a-b=c

以两个32位处理64减法,先按上述思路,计算两个数据低32位的减法, 并保存进位标志;然后计算两个数据高32位的减法,若低32位存在进位标志,高32位减法的结果还要再减1,最终得到64位的减法,分别对应的储存于高低32位地址中。


三,加法FC块(SCL)

FUNCTION "Add32To64" : Void

{ S7_Optimized_Access := 'TRUE' }

VERSION : 0.1

   VAR_INPUT 

      dwA_H : DWord;   // 数据1 高位字

      dwA_L : DWord;   // 数据1 低位字

      dwB_H : DWord;   // 数据2 高位字

      dwB_L : DWord;   // 数据2 低位字

      bSign : Bool;   // 0-无符号 1-有符号

   END_VAR


   VAR_OUTPUT 

      bOV : Bool;   // 溢出标志

      dwC_H : DWord;   // 结果1 高位字

      dwC_L : DWord;   // 结果1 低位字

   END_VAR


   VAR_TEMP 

      dwXor : DWord;

      dwAnd : DWord;

      i : Int;

      diCondition : DInt;

      dwShl : DWord;   // 移位处理

      dwCy_H : DWord;   // 进位标志

      bSignA : Bool;   // 加数1符号

      bSignB : Bool;   // 加数2符号

      bSignC : Bool;   // 和值符号

   END_VAR



BEGIN

//低位字计算

#bOV := FALSE;                           //溢出标志复位

#dwCy_H := 0;                            //进位标志清零

#dwAnd := #dwA_L AND #dwB_L;

#dwXor := #dwA_L XOR #dwB_L;

FOR #i := 1 TO 32 DO

    #diCondition := DWORD_TO_DINT(#dwAnd); //进位标志

    IF #diCondition = 0 THEN

        EXIT;                          //进位标志=0 跳出

    ELSIF #diCondition < 0 THEN

        #dwCy_H := 1;                   //进位标志首位为1,高位字+1 暂存

    END_IF;

    

    #dwShl := SHL_DWORD(IN := #dwAnd, N := 1);

    #dwAnd := #dwXor AND #dwShl;

    #dwXor := #dwXor XOR #dwShl;

END_FOR;

#dwC_L := #dwXor;

//高位字计算

#dwAnd := #dwA_H AND #dwB_H;

#dwXor := #dwA_H XOR #dwB_H;

FOR #i := 1 TO 32 DO

    #diCondition := DWORD_TO_DINT(#dwAnd); //进位标志

    IF #diCondition = 0 THEN

        EXIT;                          //进位标志=0 跳出

    ELSIF #diCondition < 0 THEN

        #bOV := TRUE;                   //进位标志首位为1,无符号计算溢出

    END_IF;

    

    #dwShl := SHL_DWORD(IN := #dwAnd, N := 1);

    #dwAnd := #dwXor AND #dwShl;

    #dwXor := #dwXor XOR #dwShl;

END_FOR;

#dwC_H := #dwXor;

//进位+1

IF DWORD_TO_DINT(IN := #dwCy_H) > 0 THEN

    #dwAnd := #dwC_H AND #dwCy_H;

    #dwXor := #dwC_H XOR #dwCy_H;

    FOR #i := 1 TO 32 DO

        #diCondition := DWORD_TO_DINT(#dwAnd); //进位标志

        IF #diCondition = 0 THEN

            EXIT;                          //进位标志=0 跳出

        ELSIF #diCondition < 0 THEN

            #bOV:=TRUE;                   //进位标志首位为1,无符号计算溢出

        END_IF;

        

        #dwShl := SHL_DWORD(IN := #dwAnd, N := 1);

        #dwAnd := #dwXor AND #dwShl;

        #dwXor := #dwXor XOR #dwShl;

    END_FOR;

    #dwC_H := #dwXor;

END_IF;

//带符号计算 溢出判断

IF #bSign THEN

    #bSignA := (DWORD_TO_DINT(#dwA_H) >= 0);

    #bSignB := (DWORD_TO_DINT(#dwB_H) >= 0);

    #bSignC := (DWORD_TO_DINT(#dwC_H) >= 0);

    #bOV := (#bSignA AND #bSignB AND NOT #bSignC) OR (NOT #bSignA AND NOT #bSignB AND #bSignC);

    //有符号计算溢出

END_IF;

END_FUNCTION


四,减法FC块(Scl)

FUNCTION "Sub32To64" : Void

{ S7_Optimized_Access := 'TRUE' }

VERSION : 0.1

   VAR_INPUT 

      dwA_H : DWord;   // 数据1 被减数 高位字

      dwA_L : DWord;   // 数据1 被减数 低位字

      dwB_H : DWord;   // 数据2 减数 高位字

      dwB_L : DWord;   // 数据2 减数 低位字

      bSign : Bool;   // 0-无符号 1-带符号

   END_VAR


   VAR_OUTPUT 

      bOV : Bool;   // 溢出标志

      dwC_H : DWord;   // 结果1 差值 高位字

      dwC_L : DWord;   // 结果1 差值 低位字

   END_VAR


   VAR_TEMP 

      dwXor : DWord;

      dwAnd : DWord;

      i : Int;

      diCondition : DInt;

      dwShl : DWord;   // 移位处理

      dwCy_H : DWord;   // 进位标志

      bSignA : Bool;

      bSignB : Bool;

      bSignC : Bool;

   END_VAR



BEGIN

//低位字计算

#bOV := FALSE;                           //溢出标志复位

#dwCy_H := 0;                            //进位标志清零

#dwXor := #dwA_L XOR #dwB_L;

#dwAnd := #dwXor AND #dwB_L;

FOR #i := 1 TO 32 DO

    #diCondition := DWORD_TO_DINT(#dwAnd); //进位标志

    IF #diCondition = 0 THEN

        EXIT;                          //进位标志=0 跳出

    ELSIF #diCondition < 0 THEN

        #dwCy_H := 1;                   //进位标志首位为1,高位字-1 暂存

    END_IF;

    

    #dwShl := SHL_DWORD(IN := #dwAnd, N := 1);

    #dwXor := #dwXor XOR #dwShl;

    #dwAnd := #dwXor AND #dwShl;

    

END_FOR;

#dwC_L := #dwXor;

//高位字计算

#dwXor := #dwA_H XOR #dwB_H;

#dwAnd := #dwXor AND #dwB_H;

FOR #i := 1 TO 32 DO

    #diCondition := DWORD_TO_DINT(#dwAnd); //进位标志

    IF #diCondition = 0 THEN

        EXIT;                          //进位标志=0 跳出

    ELSIF #diCondition < 0 THEN

        #bOV := TRUE;                   //进位标志首位为1,无符号计算溢出

    END_IF;

    

    #dwShl := SHL_DWORD(IN := #dwAnd, N := 1);

    #dwXor := #dwXor XOR #dwShl;

    #dwAnd := #dwXor AND #dwShl;

    

END_FOR;

#dwC_H := #dwXor;

//进位-1

IF DWORD_TO_DINT(IN := #dwCy_H) > 0 THEN

    #dwXor := #dwC_H XOR #dwCy_H;

    #dwAnd := #dwXor AND #dwCy_H;

    FOR #i := 1 TO 32 DO

        #diCondition := DWORD_TO_DINT(#dwAnd); //进位标志

        IF #diCondition = 0 THEN

            EXIT;                          //进位标志=0 跳出

        ELSIF #diCondition < 0 THEN

            #bOV := TRUE;                   //进位标志首位为1,无符号计算溢出

        END_IF;

        

        #dwShl := SHL_DWORD(IN := #dwAnd, N := 1);

        #dwXor := #dwXor XOR #dwShl;

        #dwAnd := #dwXor AND #dwShl;

        

    END_FOR;

    #dwC_H := #dwXor;

END_IF;

//带符号计算 溢出判断

IF #bSign THEN

    #bSignA := (DWORD_TO_DINT(#dwA_H) >= 0);

    #bSignB := (DWORD_TO_DINT(#dwB_H) >= 0);

    #bSignC := (DWORD_TO_DINT(#dwC_H) >= 0);

    #bOV := (#bSignA AND NOT #bSignB AND NOT #bSignC) OR (NOT #bSignA AND #bSignB AND #bSignC);

    //有符号计算溢出

    

END_IF;

END_FUNCTION


五,测试,用16#7FFF FFFF FFFF FFFF  加/减 16#8000 0000 0000 0000 带符号运算。在VBA,用longlong(64位有符号整数)简单验证了几个数字,都还对的上。


六,关于溢出判断,

无符号的运算,进位"1"左移超出最高字(Dword)的31位,判定溢出。

有符号运算溢出,

加法,( 加数A正 A 加数B正  A  和值C负 ) O( 加数A负 A 加数B负  A  和值C正  ),

减法,( 被减数A正 A 减数B负  A  和值C负 ) O( 被减数A负 A 加数B正  A  和值C正  )。


有符号运算的溢出判断差不多算“枚举”特例了,担心有纰漏,这里用什么方式最好?


关于64位整数的加减运算 已锁定
编辑推荐: 关闭

请填写推广理由:

本版热门话题

SIMATIC S7-1200系列

共有15376条技术帖

相关推荐

热门标签

相关帖子推荐

guzhang

恭喜,你发布的帖子

评为精华帖!

快扫描右侧二维码晒一晒吧!

再发帖或跟帖交流2条,就能晋升VIP啦!开启更多专属权限!

  • 分享

  • 只看
    楼主

top
X 图片
您收到0封站内信:
×
×
信息提示
很抱歉!您所访问的页面不存在,或网址发生了变化,请稍后再试。