将 LREAL 转换为二进制并将其解释为以 10 为基数的 LINT

Converting LREAL to binary and interpreting it as base 10 LINT

运行这里是“CODESYS V3.5 SP16”,我正在尝试实现

中提到的hashcode算法

Best implementation for hashCode method for a collection

并且必须构建我自己的解决方案来复制 Java 的 Float.floatToIntBits(f),从而产生以下功能

FUNCTION F_lrealToLintBits : LINT
VAR_INPUT
  lrVal : LREAL;
END_VAR
VAR
  arrBytes : ARRAY[0..7] OF BYTE; // LREAL contains 64 bits and each byte contains 8 bits
  pVal     : POINTER TO LREAL := ADR(arrBytes);
  diIndx   : DINT;
  uiExpt   : UINT := 0; // exponent goes from 0 to 63
END_VAR
pVal^ := lrVal; // maps LREAL to array of bytes

// little endian? cause it seems that least significant bit is at lowest address
FOR diIndx := LOWER_BOUND(arrBytes, 1) TO UPPER_BOUND(arrBytes, 1) BY 1 DO
  // bit access seems to be manual only so no loops
  IF arrBytes[diIndx].0 THEN
    F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
  END_IF

  uiExpt := uiExpt + 1; // have to increment exponent after every bit

  IF arrBytes[diIndx].1 THEN
    F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
  END_IF

  uiExpt := uiExpt + 1; // have to increment exponent after every bit

  IF arrBytes[diIndx].2 THEN
    F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
  END_IF

  uiExpt := uiExpt + 1; // have to increment exponent after every bit

  IF arrBytes[diIndx].3 THEN
    F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
  END_IF

  uiExpt := uiExpt + 1; // have to increment exponent after every bit

  IF arrBytes[diIndx].4 THEN
    F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
  END_IF

  uiExpt := uiExpt + 1; // have to increment exponent after every bit

  IF arrBytes[diIndx].5 THEN
    F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
  END_IF

  uiExpt := uiExpt + 1; // have to increment exponent after every bit

  IF arrBytes[diIndx].6 THEN
    F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
  END_IF

  uiExpt := uiExpt + 1; // have to increment exponent after every bit

  IF arrBytes[diIndx].7 THEN
    F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
  END_IF

  uiExpt := uiExpt + 1; // have to increment exponent after every bit
END_FOR

首先,我想知道在某些库中是否已经存在这样的函数?

我查看了很多地方,包括 OSCAT“Basic, 3.31”库,但找不到任何类似的东西(即使是一组我可以链接在一起的函数也可以)。

其次,位访问只能手动完成吗?

我更愿意使用循环,但似乎这不可能?如果数组中的数据类型从 BYTE 变为其他类型(例如 DWORD),是否有较少的复制和粘贴访问位的方法涉及自动检测位数?

潜在替代方案

似乎这是联合派上用场的地方,如 https://forge.codesys.com/forge/talk/Engineering/thread/02a65a50b2/#e5f4/1911/c524 中所述,如果值不是来自外部来源(即无需检查字节顺序),Float.floatToIntBits(f) 会像

一样简单
TYPE
  U_lrealToRawLintBits :
  UNION
    lrVal : LREAL;
    liVal : LINT;
  END_UNION
END_TYPE
// supposed to replicate floatToIntBits() in Java at
// https://github.com/openjdk/jdk/blob/739769c8fc4b496f08a92225a12d07414537b6c0/src/java.base/share/classes/java/lang/Float.java#L775
FUNCTION F_lrealToLintBits : LINT
VAR_INPUT
    lrVal : LREAL;
END_VAR
VAR
    uLrealToLintBits : U_lrealToRawLintBits; // for interpreting LREAL as base 10 LINT
END_VAR
IF FPU.IsLRealPosInfinity(lrVal) THEN
  F_lrealToLintBits := 2139095040; // corresponds to 0x7f800000
ELSIF FPU.IsLRealNegInfinity(lrVal) THEN
  F_lrealToLintBits := 4286578688; // corresponds to 0xff800000
ELSIF FPU.IsLRealNaN(lrVal) THEN
  F_lrealToLintBits := 2143289344; // corresponds to 0x7fc00000
ELSE
  uLrealToLintBits.lrVal := lrVal;
  F_lrealToLintBits := uLrealToLintBits.liVal;
END_IF

我认为您要实现的目标是将 LREAL 作为输入,但其中的位实际上是 LINT 的位,但进行了字节交换。

如果是这样,应该有更直接的解决方案。这是一个例子:

FUNCTION lreal_to_int64 : LINT
VAR_INPUT
  value: LREAL;
  value_is_little_endian: BOOL;
END_VAR
VAR
  no_swap: BOOL;
  source_bytes, target_bytes: POINTER TO BYTE;
  value_as_lint: POINTER TO LINT;
END_VAR

{IF defined (IsLittleEndian)}
  no_swap := value_is_little_endian;
{ELSE}
  no_swap := NOT value_is_little_endian;
{END_IF}

IF
  no_swap
THEN
  value_as_lint := ADR(value);
  lreal_to_int64 := value_as_lint^;
  RETURN;  
END_IF

target_bytes := ADR(lreal_to_int64);
source_bytes := ADR(value);
target_bytes[0] := source_bytes[7];
target_bytes[1] := source_bytes[6];
target_bytes[2] := source_bytes[5];
target_bytes[3] := source_bytes[4];
target_bytes[4] := source_bytes[3];
target_bytes[5] := source_bytes[2];
target_bytes[6] := source_bytes[1];
target_bytes[7] := source_bytes[0];

当然可以使用按位运算符以编程方式访问位。尝试这样的事情。

FUNCTION get_bit : BOOL
VAR_INPUT
  value: LWORD;
  bit_position: USINT;
END_VAR
VAR
 shifted: LWORD;
END_VAR

shifted := SHR(value, bit_position);
get_bit := shifted.0;