Ask for good ideas about handling floating point number!

BTW, the simplest solution is to bump (increase) the tolerance by one-tenth (or one-half) of the lowest resolution decimal digit, i.e. use 0.0011 (or 0.0015); this is functionally equivalent to the multiply-by-1k-into-DINTs.

Since the scanned or measured value will always be within 1 bit (one part in eight million) of three decimal places to the right of the decimal point and all zeros after that, 0.015-roundoff_epsilon will be accepted and 0.014+roundoff_epsilon will be rejected.

ADD scanval 0.0011 testval GRT testval 0.016 SUB scanval 0.0011 testval LES testval 0.016 OTE scanval_in_range


or

ADD scanval 0.0011 testvalhi SUB scanval 0.0011 testvallo LIM testvallo 0.016 testvalhi OTE scanval_in_range

 
Last edited:
Wow,

Go away and do some work and there are 2 pages of posts to go through.

Since I mentioned using a function to do a comparison of a value to a target, I worked up an example. I don't have Rockwell stuff but I think this should be understandable to anyone with a little experience.

I have used functions like this a lot to check values against a target.

This is a link to a video of the demo that I worked up.

https://youtu.be/AQbJaQFQYRk
 
Yes WoW!,
2 pages of what is so simple, like Norm, I created a function the same but added two other outputs value too low & value too high (often used as alarms or messaging), what could be simpler, I have seen a few examples where coders have used floats but equals as the target, floats can vary so unless it matches exactly it will not be true seen this on batching systems & also even integers where encoders are used, compare for greater than and less than if out of tolerance then alarm & stop the process,
 
Answering the question

OP mentioned AOIs in the OP, so they likely already know how to make functions; that is the trivial part.

The actual issue of this thread is the round-off in binary floating point representation when the magnitude of the nominal center of range (0.016) is an order of magnitude or more larger than that of the tolerance (0.001). This requires knowledge about IEEE-754 floating-point representation, as noted by @nzdied1 early on in the thread; see also this wiki. The issue is at it's core only a visual/HMI problem, not an actual problem, as it involves the binary floating-point goalposts being offset by a whisker from what the rational number symbols, such as what an operator would see ("0.001" and "0.016" and "0.015"), imply mathematically.

There are a at least a few ways to handle it:
  • Bump the tolerance out in the next decimal place
  • Bump the limits to add or subtract a bit (binary digit, not "a little bit") to effectively bump the tolerance; I used the ratio (16M-1)/16M to do this; it could also be done with COP/CPW instructions and integer math.
  • Convert fixed-point by scaling fractional decimal parts of floating-point values up to integers, in this case multiply by 1k, since actual values have resolution down to milli-units.
    • Note that, though unlikely in a typical process seen by a PLC, this does carry a risk of a PLC fault. This is the approach that ended the Deep Impact spacecraft mission: the mission was doomed shortly after (2**32 / 10) seconds into the millennium (I've seen the code).
 
BTW, the simplest solution is to bump (increase) the tolerance by one-tenth (or one-half) of the lowest resolution decimal digit, i.e. use 0.0011 (or 0.0015); this is functionally equivalent to the multiply-by-1k-into-DINTs.

Since the scanned or measured value will always be within 1 bit (one part in eight million) of three decimal places to the right of the decimal point and all zeros after that, 0.015-roundoff_epsilon will be accepted and 0.014+roundoff_epsilon will be rejected.

ADD scanval 0.0011 testval GRT testval 0.016 SUB scanval 0.0011 testval LES testval 0.016 OTE scanval_in_range


or

ADD scanval 0.0011 testvalhi SUB scanval 0.0011 testvallo LIM testvallo 0.016 testvalhi OTE scanval_in_range

Aha, it is very helpful of you, I get your point, I will go and see what I get with it.

And this post acts also as a map, to anyone who search and come to here in the future, to find a useful answer, hopefully
 
Guys, sorry I delayed the test for some days, just now I had your suggestions tested a bit, but I saw problems, however I am not sure it's just me implementing wrong or what.

(1)
First thing is, in my examplle I used 0.016 / 0.001 for the numbers, but in real the fractional part is not fixed, we may have 0.0000315 for instance, so the question will be how to determine what factor should be applied. If I make it a large fixed number, it is not always working for other nums:

MUL 10.315 10000000 --- OUTCOME IS 103149992

(2)
For the same reason, I think I am unable to extract lowest resolution decimal digit, (the method was proposed by drbitboy), so I guess in practise I cannot actually do the bump(increase) the tolerance by one-tenth/one-half?? Also

(3)
grams := trunc((kg*1000)+0.5);

will not always work either?

(4)
And as for the MUL and then DIV ratio = 16777215.0 / 16777216.0 thingy, also proposed by drbitboy, testing using 0.015 is ok, but I tried another num and then it failed:

10.315 - 0.011 = 10.304
10.304 * 16777215 / 16777216 = 10.303999
OR
10.304 * 16777216 / 16777215 = 10.304001

(5)
As I think and try more, I was wodnering if I can convert real numbers into string type and program math logic specifically, but that's not possible either:

RTOS 30.315 --- OUTCOME IS 30.3150005

As programmer cannot predict how many digits are in the fractional part, I can neither extract effective characters out of the outcome string.


What do you guys suggest next?
 
Try this although not CLX you should be able to re-code it, this works & gives 3 decimal places or by changing the fixed value of 1000 to say 100 will give 2 decimal places.

3 Dec Places.png
 
(4)
And as for the MUL and then DIV ratio = 16777215.0 / 16777216.0 thingy, also proposed by drbitboy, testing using 0.015 is ok, but I tried another num and then it failed:

10.315 - 0.011 = 10.304
10.304 * 16777215 / 16777216 = 10.303999
OR
10.304 * 16777216 / 16777215 = 10.304001


You need to multiply only the lower limit (target - tolerance) by 16777215 first, then divide by 16777216 last, to get the corrected lower limit.



Then multiply the upper limit (target + tolerance) by 16777216 first, then divide by 16777215 last, to get the corrected upper limit.


Also, using 8388607/8388608 for the lower limit, and 8388608/8388607 for the upper limit, might work better.



This assumes all targets and tolerances are positive values.
 
What do you guys suggest next?

If the location of the least significant digit is variable, then all of the *1E+nnn tricks will no longer work.

So we need to work with the bits in the floating-point target and limit values; in order to do that, we need to convert REALs to DINTs, and back again, using the COP or CPW instructions.

Create an AOI called BUMP:

  • Input rin, REAL
  • Input ibump, DINT
  • Output rout, REAL
  • Internal tag idint, DINT; can be temporary (not static)
  • Code, 1 rung:
    • CPS rin ibump 1 ADD idint ibump idint CPS idint rout 1
Create a second AOI called CALCULATE_LIMITS

  • Input target, REAL
  • Input tolerance, REAL N.B. must be positive
  • Output lolimit, REAL
  • Output hilimit, REAL
  • Internal tag lotarget, REAL
  • Internal tag hitarget, REAL
  • Internal tag calctol, REAL
  • N.B. Internal tags can be temporary (not static)
  • Code, 5 rungs:
    • BUMP target -1 lotarget BUMP target 1 hitarget
    • SUB lotarget tolerance lolimit
    • LBL loloop SUB lotarget lolimit calctol LEQ calctol tolerance BUMP lolimit -1 lolimit JMP loloop
    • ADD hitarget tolerance hilimit
    • LBL hiloop SUB hilimit hitarget calctol LEQ calctol tolerance BUMP hilimit 1 hilimit JMP hiloop
Caveats

  • It may not be necessary to bump the target
  • the LEQs perhaps could be LESs
 
Last edited:
You need to multiply only the lower limit (target - tolerance) by 16777215 first, then divide by 16777216 last, to get the corrected lower limit.



Then multiply the upper limit (target + tolerance) by 16777216 first, then divide by 16777215 last, to get the corrected upper limit.


Also, using 8388607/8388608 for the lower limit, and 8388608/8388607 for the upper limit, might work better.



This assumes all targets and tolerances are positive values.

I guess your intention is to find the number which cacn be precisely represented, also is the nearest to the given low limit/high limit?
 
I guess your intention is to find the number which cacn be precisely represented, also is the nearest to the given low limit/high limit?

yes, pretty much. the 8M ratio would probably do better.

the bit fiddling on my last post would work best, I am pretty sure, but is more involved.
 
If the location of the least significant digit is variable, then all of the *1E+nnn tricks will no longer work.

So we need to work with the bits in the floating-point target and limit values; in order to do that, we need to convert REALs to DINTs, and back again, using the COP or CPW instructions.

Create an AOI called BUMP:

  • Input rin, REAL
  • Input ibump, DINT
  • Output rout, REAL
  • Internal tag idint, DINT; can be temporary (not static)
  • Code, 1 rung:
    • CPS rin ibump 1 ADD idint ibump idint CPS idint rout 1
Create a second AOI called CALCULATE_LIMITS

  • Input target, REAL
  • Input tolerance, REAL N.B. must be positive
  • Output lolimit, REAL
  • Output hilimit, REAL
  • Internal tag lotarget, REAL
  • Internal tag hitarget, REAL
  • Internal tag calctol, REAL
  • N.B. Internal tags can be temporary (not static)
  • Code, 5 rungs:
    • BUMP target -1 lotarget BUMP target 1 hitarget
    • SUB lotarget tolerance lolimit
    • LBL loloop SUB lotarget lolimit calctol LEQ calctol tolerance BUMP lolimit -1 lolimit JMP loloop
    • ADD hitarget tolerance hilimit
    • LBL hiloop SUB hilimit hitarget calctol LEQ calctol tolerance BUMP hilimit 1 hilimit JMP hiloop
Caveats

  • It may not be necessary to bump the target
  • the LEQs perhaps could be LESs

Ok, I will have this method tested tomomrrow; this is to bump the low limit and high limit to my understanding, and then I can simply compare them with the given/measured value; if I am wrong pls correct me
 

Similar Topics

I have a program that I've used 100 times. SQO settings: File N7:0, Mask 0FFFFh, Dest B3:1, Control R6:0, Length 8, Pos 2. Length & Position...
Replies
48
Views
942
We are to develop a first application in Codesys. It will contain motion (Softmotion) with drives on Ethercat (CSP mode). Off course there will be...
Replies
2
Views
905
Hi. Rockwell learning curve 132-1b. I was having trouble to change IP address on a EN2TR. Finally found out that I need to change the IP...
Replies
1
Views
746
Hi guys.. I am using Vijeo Citect 7.4.. I am doing a function inside a function.. What I want to do is I want to put a function to sleep but want...
Replies
7
Views
1,435
I am working with a 1768-ENBT and I was able to connect to it through my laptop. My laptop IP is 192.168.1.10 subnet 255.255.255.0 and the PLC...
Replies
12
Views
1,553
Back
Top Bottom