LREAL bits and/or WORDs

I have a 48 bit unsigned integer that I’m receiving in three WORDs of data (via Modbus). I need to combine these into one number (Flow Total). I’ve tried accessing the LREAL variable by its bits:

lrealVariable.0 := wordVariable.0;
lrealVariable.1 := wordVariable.1;
etc.

But it seems the bits of the LREAL variable are not accessible the same way the WORD bits are.

I then tried making an array of four WORDs but AS will not equate the LREAL to the array of words:
lrealVariable := arrayVariable;

Possibly a case for a Derived Data Type…but I’m having trouble sorting out the help document surrounding that. I’ve created Structures many times but is there a way to create two types of data that point at the same memory addresses?
Any advise is appreciated.
Thanks

Hi David,

You can check about the function brsmemcpy, ou will be able to copy memory
https://help.br-automation.com/#/en/4/libraries%2Fasbrstr%2Ffbks%2Fbrsmemcpy.html

If I take your example:

brsmemcpy(ADR(lrealVariable),ADR(wordVarialble1),SIZEOF(WORD));
brsmemcpy(ADR(lrealVariable)+SIZEOF(WORD),ADR(wordVarialble2),SIZEOF(WORD));
brsmemcpy(ADR(lrealVariable)+SIZEOF(WORD)*2,ADR(wordVarialble3),SIZEOF(WORD));

1st you copy memory of the wordVariable1 (2 bytes) into 2 first bytes of the lrealVariable.
2nd you copy memory of the wordVariable2 (2 bytes) into bytes 3 and 4 of the lrealVariable
Finally you copy memory of the wordVariable3 (2 bytes) into bytes 5 and 6 of the lrealVariable

If you want you can avoid using SIZEOF function that for the explanation

Thanks for the reply Florent. I copied your example and substituted my variables. It compiles and I can enter values into wordVariable1,2 and 3 but the value in lrealVariable is not as expected. I am entering a value of 1 into the word variables, one at a time and all instances give me a very small number (e.g. 4.94065E-324).

What I’m expecting(hoping) to see is if:
wordVariable1 = 1
wordVariable2 = 0
wordVariable3 = 0
binary representation of lrealVariable is: 0000 0000 0000 0001
lrealVariable value = 1

or

wordVariable1 = 0
wordVariable2 = 1
wordVariable3 = 0
binary representation of lrealVariable is: 0000 0000 0001 0000
lrealVariable = 16

etc.

I’m still wrapping my head around brsmemcpy so please let me know if I’m misunderstanding. I’ll continue to work with it but any insight is appreciated.
Thanks
Dave

A Floatingpoint number consists of Exponent and Mantisse. It cant be created by a simple copy of a Bitpattern as far as i think.

LREAL

First you have to finde the Format which B&R is using. I guess IEEE754 but i don’t know it for sure.

Wiki LREAL

Sign 1 Bit
Mantisse 52 Bits
Exponent 11 Bits
= 64Bits

Zahl = m × 2 ^ e.

I hope the information helps to convert the Data.

German Doku in other Wiki

Greetings Michael

Just adding that I like the following IEEE-754 floating point converter for checking what number(s) can be represented as floating point/calculating floating point error:

IEEE-754 Floating Point Converter (h-schmidt.net)

In your case you can also just copy directly to the mantissa if you know the bits of the mantissa. You also need to have an exponent of 1 in that case.

2 Likes

Oh yeah, that’s my bad!
I answered too fast, bit representation of float64 isn’t the same as uint64.
Thanks @michael.bertsch and @eric.oldfield for this !
I will create an example and test it !

Ok I’ve a simple implementation for you.

lreal1	:=	WORD_TO_LREAL(word3) * brmpow(2,32); // Shift of 32 bits
lreal1	:=	lreal1 + WORD_TO_LREAL(word2) * brmpow(2,16); // Shift of 16 bits
lreal1	:=	lreal1 + WORD_TO_LREAL(word1); // No shift

You need to add AsBrMath library to have the brmpow function.

What is done:

  • You convert WORD decimal value to LREAL and assign it to the LREAL variable and shift the value by 32 bits using multiplication by 2^32
  • Next same thing with a shift of 16 bits and add result to the LREAL variable
  • Last step same thing without shifting

Hope it solved your problem !

1 Like

I don’t think you answered incorrectly, given the information in the original post. My read is that it seems to be what David was looking for.

The question that’s been bugging me, is who gives 48/64 bits of a floating-point value and expects it to be in any way accurate. Are we to assume the last 16-bits in the Mantissa are all 0’s? Then why not use a 32-bit floating-point and have roughly the same accuracy while using 1 fewer register? It’s weird.

If I were to guess, those 48-bits actually need to be manually converted (ex. interpret as a 48-bit long integer, then divide by 10 or 1000), but that would be part of the information from the Modbus device.

Bonus thought: If the data point is total (accumulated) flow, a basic value that always counts up, then floating-point values losing precision at higher values wouldn’t be ideal. Integers work better, even if they eventually roll over.

-Austin

Thanks for all of the replies.

I knew about the formatting on floats…so I should have clued into that.

To Austin’s point:
This is the first time I’ve seen this format from a flow meter as well (McNaught flow meter). And you are correct, it is in fact a UINT48 value and I then need to apply three decimal places.
e.g. - If the total is 1,111,111.111 the data is 0x0000 0x423A 0x35C7

The reason I chose to use the LREAL data type is that it’s the only data type larger than 32 bits that I can see…? I should have included that information in the original post…perhaps there’s a better way? I’m wide open to other suggestions.

I’ll see if I can get Florent’s revised code to work in the meantime.

Thanks again
Dave

My implementation certainly not the most efficient nor the most optimized, but it should do the job.

1 Like

This worked great Florent, thanks!

Dave

1 Like

Another possibility is to make a function in C, because there we have a 64 bit integer.
This way we can convert up to 8 bytes / 4 words / 2 longs into an LREAL.

Function declaration and body in a library:
image

double BYTEsToLREAL(plcbyte Input[8])
{
/* convert input array to 64 bit integer */
long long int value = * ((long long int *)&Input[0]);
return ((double)value);
}

In the task calling the function:
image

brsmemcpy(ADR(BYTEs), ADR(WORDs),SIZEOF(WORDs));
ValueLREAL := BYTEsToLREAL(BYTEs);

2 Likes

You’re right, it’s cleaner and more reusable!