Converting LREAL to binary and interpreting it as base 10 LINT

void
2022-01-13
2022-01-20
  • void - 2022-01-13

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

    https://stackoverflow.com/questions/113511/best-implementation-for-hashcode-method-for-a-collection/113600#113600

    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)?

     
  • i-campbell

    i-campbell - 2022-01-13

    For reference, here is the OpenJDK implementation and description of that function:
    https://github.com/openjdk/jdk/blob/739769c8fc4b496f08a92225a12d07414537b6c0/src/java.base/share/classes/java/lang/Float.java#L743

    I guess it depends if you want your hash to collide all the NaN values together or not? If you do not want them to collide, you might want to use instead floatToRawIntBits.

    You could use the library FloatingPointUtils function IsLRealNaN.

    As the endinanness should be the same for the LREAL and the LINT, you could just do a memory copy, or even use a UNION to do the equivelant of floatToRawIntBits.

    I do not understand how your code, which does some exponent play, relates to Float.floatToIntBits of the openjdk.

    let me know what you think or if I have mis-interpreted.

     
    • void - 2022-01-20

      Hey @i-campbell, thanks for taking a look

      I think you're right in that I am actually implementing Float.floatToRawIntBits(f) as seen in https://github.com/openjdk/jdk/blob/739769c8fc4b496f08a92225a12d07414537b6c0/src/java.base/share/classes/java/lang/Float.java#L782

      What I actually did was actually get some inputs and outputs e.g.
      - Float.floatToRawIntBits(1.5f) should return
      Β Β Β Β  - 4609434218613702656 for double precision float or
      Β Β Β Β  - 1069547520 for single precision float
      - Float.floatToRawIntBits(20.12f) should return
      Β Β Β Β  - 4626356494213547295 for double precision float or
      Β Β Β Β  - 1101067715 for single precision float

      and implemented my solution based on that

      But yes, you are correct in that I should use IsLRealNaN to handle NaNs (didn't even cross my mind to do that)

      Nevertheless, I guess there's no function (or set of functions) in some library that takes the above input and gives the above output?

       
      • i-campbell

        i-campbell - 2022-01-20

        MemCpy works.

        PROGRAM PLC_PRG
        VAR
            myLINT: LINT;
            myLINT2: LINT;
            myLREAL: LREAL := 1.5;
            myLREAL2: LREAL := 20.12;
            myDINT: DINT;
            myDINT2: DINT;
            myREAL: REAL := 1.5;
            myREAL2: REAL := 20.12; 
        END_VAR
        
        MEMUtils.MemCpy(pbyDest:=ADR(myLINT),pbySrc:=ADR(myLREAL),SIZEOF(myLINT));
        MEMUtils.MemCpy(pbyDest:=ADR(myLINT2),pbySrc:=ADR(myLREAL2),SIZEOF(myLINT));
        
        MEMUtils.MemCpy(pbyDest:=ADR(myDINT),pbySrc:=ADR(myREAL),SIZEOF(myDINT));
        MEMUtils.MemCpy(pbyDest:=ADR(myDINT2),pbySrc:=ADR(myREAL2),SIZEOF(myDINT));
        
         

        Last edit: i-campbell 2022-01-20
      • i-campbell

        i-campbell - 2022-01-20

        You can do it without a library and without any implementation, using a UNION

        TYPE LINTLREAL :
        UNION
            asLint : LINT;
            asLreal : LREAL;
        END_UNION
        END_TYPE
        
        PROGRAM PLC_PRG_2
        VAR
            myLintLreal : LINTLREAL := (asLreal := 1.5);
            myLintLreal2 : LINTLREAL := (asLreal := 20.12);
        END_VAR
        
        // no implementation part required.
        
         
        πŸ‘
        1
  • void - 2022-01-20
     

    Last edit: void 2022-01-20
    • i-campbell

      i-campbell - 2022-01-20

      I disagree that you need to add a check for endianness. For Big Endian systems, both the LINT and the LREAL are byteswapped.

       
      • fajean - 2022-01-20

        This is OK as an assumption if both values were created by the CPU. The endinanness check is intended for cases when a value comes from an external source (e.g. fieldbus) the endianness of which does not match the CPU. The intent of the OP seemed to assume both values were byte swapped, but obviously if they are guaranteed to share the same endianness the code becomes only a two-line pointer manipulation.

         

        Last edit: fajean 2022-01-20

Log in to post a comment.