一些应用场景对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正 )。
有符号运算的溢出判断差不多算“枚举”特例了,担心有纰漏,这里用什么方式最好?