Search code examples
codesysstructured-text

Converting LREAL to binary and interpreting it as base 10 LINT


Running "CODESYS V3.5 SP16" here, I am trying to implement the hashcode algorithm mentioned in

Best implementation for hashCode method for a collection

and had to built my own solution to replicate Java's Float.floatToIntBits(f) which resulted in the following function

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

Firstly, I would like to know if there is an existing function in some library that does this already?

I looked in many places including in the OSCAT "Basic, 3.31" library but could not find anything similar (even a set of functions that I could chain together would be OK).

Secondly, can bit access only be done manually?

I would prefer to use a loop but it seems that is not possible? Is there a less copy-and-paste method of accessing the bits that would involve automatically detecting the number of bits if the data type in the array changes from BYTE to something else (e.g. DWORD)?

Potential alternative

Seems like this is where unions come in handy, as mentioned in https://forge.codesys.com/forge/talk/Engineering/thread/02a65a50b2/#e5f4/1911/c524 where if the value does not come from an external source (i.e. no need to check for endian-ness), Float.floatToIntBits(f) would be as simple as

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

Solution

  • I think what you are trying to achieve is to take as input an LREAL but with the bits inside really being those of a LINT, but byte-swapped.

    If so, there should be a more straightforward solution. Here is an example :

    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];
    

    Programmatic access to bits is certainly possible using bitwise operators. Try something like this.

    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;