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