The compare-equal contact tests the whole stored REAL, so digits hidden by a 3-decimal data view still break the match. Stop using exact equality for in-position logic: compute lower and upper limits at the target plus and minus a tolerance sized from the positioning spec, and drive In Position from that range test.
An In Position light that stays dark while target and actual look identical on the panel is not a broken compare. The compare-equal contact (SP61 on the controllers that use that label) tests the two stored REAL values in full, and the 3-decimal data view is presentation only. The fix is to stop asking for exact equality: build the indication from a deadband window, target plus and minus a tolerance sized from the machine's positioning requirement. This page sizes that window, works the arithmetic on a 1.654 target, and shows the two-limit logic and the boundary checks that verify it.
What the compare-equal contact actually tests
A compare-real instruction and its equal contact evaluate the numbers as stored, all bits, on every scan. The data view showing 3 decimal places is a display format: it hides digits and changes nothing in the controller. So 1.654001 against a 1.654 target reads as a match on screen and fails in the logic, because the two stored values differ in the fourth through sixth decimals. Raising the decimal count on the panel changes what you see, never what the contact sees; the article on showing more decimal places on an HMI numeric display covers that setting.
Why a REAL misses the exact match
A REAL in these controllers is IEEE 754 single precision: 32 bits as a sign, an exponent and a 23-bit fraction. The format stores the nearest representable value, and most decimal fractions have no exact binary form. A target typed as 1.654 lands a hair off 1.654, and a calculated position that should equal it lands a hair off that hair, a few steps apart in the last bits.
The steps have a size. Between 1 and 2 the spacing is 2^-23, about 0.00000012, so a residual of 0.000001 is roughly eight of them. Spacing doubles with each doubling of magnitude: between 128 and 256 it is 2^-16, about 0.000015, which covers readings from 128 up through 200.000000000. Near 150, then, consecutive representable values sit about 0.000015 apart, and a sixth-decimal offset there falls below the format's resolution. Near 1.654 the same offset is several resolution steps. Both cases are the format doing its job, and neither is a fault to repair.
Multiplying every operand by 100, comparing, then dividing by 100 looks like a fix and can pass for days. Each multiply and divide is more float arithmetic that can leave its own last-bit error, and a 0.000001 offset reappears the first time a calculation lands awkwardly. Scaling also never decides which differences the machine should accept; that decision is the tolerance, and it belongs in a window.
On many controllers a REAL travels as two 16-bit word registers, and the storage layout matters when you move one between loops; displaying a PLC float stored in two word registers on an HMI covers that case.
Symptoms and what each points to
Each failure below has one cause in the stored value or in the window. Check them in this order:
- The equal contact stays open while the data view shows matching numbers. The stored REALs differ in digits the 3-decimal view hides. Fix it with the window.
- Typing the target with trailing digits, 1.654001 against 1.654, makes it pass. The typed constant then matches the calculated value to the last stored bit, until the next calculation moves those digits. Constants are not a fix; the window is.
- Multiply by 100, compare, divide by 100 works, then fails again with a 0.000001 offset. The extra float arithmetic adds its own low-bit error. Compare in engineering units with the window.
- In Position flickers near the target while Move Left and Move Right behave. The actual sits on the equality boundary and the loop dithers the last bits. The window absorbs the dither.
- In Position latches but the part measures out of tolerance. The window is wider than the process allows. Shrink the tolerance to the spec value.
Size the window from the positioning requirement
Read the tolerance off the machine or process spec, in the same units as the target: a spec that says the stop must land within 0.001 mm gives a window of target plus and minus 0.001 mm. If nobody wrote a number, measure it: run the stop ten cycles, record the worst offset between target and actual in the data view, and set the window just above that worst case.
Two bounds keep the window honest. It must be wider than the largest low-bit residual the data view shows, or the indication keeps hunting at the edges. It must sit at or inside the process tolerance, or the light promises a position the part does not hold. With a 0.000001 residual and a 0.001 window, the margin is 1000 to 1.
If the position reaches the panel through a scaling chain, settle the units first; the bookkeeping in PLC pressure scaling that shows three decimal places is the same job.
Example: a 1.654 mm target with a 0.001 mm window
The inputs, with units and where each comes from:
- Target: 1.654 mm, the magnitude of the failing case at the top of this page.
- Tolerance: 0.001 mm, from the positioning spec: the stop must land within 0.001 mm of target.
- Actual, case A: 1.654001 mm, a stored value carrying a 0.000001 mm float residual, as described above.
- Actual, case B: 1.645 mm, a stop that settled 0.009 mm short.
- Actual, case C: 1.656 mm, a stop 0.002 mm over.
The rule in words: the axis is in position when actual is at or above lower and at or below upper, with lower at target minus tolerance and upper at target plus tolerance. Below lower lights Move Left; above upper lights Move Right.
The arithmetic:
- lower = 1.654 - 0.001 = 1.653 mm; upper = 1.654 + 0.001 = 1.655 mm.
- Case A: error = 1.654001 - 1.654 = 0.000001 mm; 0.000001 <= 0.001, so In Position latches. Under exact equality the same pair failed, because 1.654001 and 1.654000 are different stored values. A 0.000001 mm residual is one nanometre: arithmetic noise, not motion.
- Case B: error = 1.645 - 1.654 = -0.009 mm; 0.009 > 0.001 and the sign is negative, so the value is below the window: Move Left.
- Case C: error = 1.656 - 1.654 = 0.002 mm; 0.002 > 0.001, above the window: Move Right.
The result: In Position passes the residual case with three orders of magnitude to spare and stays off on genuine 0.009 mm and 0.002 mm misses. Each light has exactly one state and no two overlap.
The decision: hold the window at 0.001 mm while the spec says so. If the spec loosens to ±0.01 mm, recompute the bounds to 1.644 mm and 1.664 mm; the sixth-decimal digits stay out of the decision at either size.
Build the two-limit compare
Compute the bounds once from the target, then let the states fall out of two compares:
target : REAL, the commanded stop, 1.654 mm
tolerance: REAL, from the spec, 0.001 mm
actual : REAL, live axis position, mm
lower : REAL, target - tolerance = 1.653 mm
upper : REAL, target + tolerance = 1.655 mm
if actual < lower then state := BELOW
elsif actual > upper then state := ABOVE
else state := IN_POSITION
The syntax is illustrative; use the compares, subtracts and the absolute-value instruction your controller offers. Drive Move Left from BELOW, In Position from IN_POSITION and Move Right from ABOVE; the states are exclusive by construction, so no two lights can set on the same scan.
Keep the signed error as well if the logic needs direction: error = actual - target, and a negative error means below. Take magnitude from the controller's absolute-value instruction. Leave the sign bit alone: in IEEE 754 single precision the sign is bit 31, and hand-flipping bits turns a clean zero into a negative zero that compares equal to zero and hides the difference you were chasing.
Keep the exact-equal contact only where equality itself is the requirement, such as matching an integer setpoint word, where every value below 16,777,216 is stored exactly. When a compare misbehaves for a storage reason instead, the same discipline applies, as in the fix for a PLC hour compare that needed a single word, not a double.
Checks at and around the limits
Run the checks in the data view with the decimal count raised so no digit hides:
- Show target, lower, upper, actual and the state at once.
- Force three actuals: one below lower, one between the bounds, one above upper. Only the middle case may set In Position.
- Set actual exactly on each bound. With the inclusive compare above, actual at 1.653 mm or 1.655 mm still latches In Position; if your policy is exclusive, decide it here and write it down.
- Jog the axis across the target and watch all three lights: each sets in turn and no two ever set together.
- Repeat with a typed constant target and a calculated target. The window must give the same states for both, residual digits included.
If the states are right in the controller and the panel still shows the old behaviour, the panel tag is pointed at the wrong register; that is a tag mapping check, not a logic problem.
The decision
Recommendation: drive In Position from the two-limit window: lower at target minus tolerance, upper at target plus tolerance, with the tolerance read from the machine's positioning spec, and remove the exact-equal contact from the indication logic. If no spec number exists, cycle the stop ten times, read the worst offset in the data view, and set the window just above it. Keep the exact contact only where equality itself is the requirement, such as an integer setpoint match.
Questions
Does multiplying by 100 and dividing back fix the compare?
No. Each multiply and divide is another floating-point operation that can leave its own low-bit error, so a 0.000001 offset can return the next time the value is calculated. Scaling also changes the units inside the math: with every operand multiplied by 100, a 0.001 window would have to be written as 0.1, which is easy to get wrong. The window compare needs no scaling and stays in engineering units end to end.
Why did typing 1.654001 into the target make the light work?
The extra digits made the typed constant match what the calculation produced, so the stored values agreed to the last bit and the equal contact closed. It only holds until the next calculation moves the final digits again, and then the contact drops for no visible reason. Chasing stored values with typed constants is a diagnostic at best; the deadband window removes the chase.
Can a stored REAL have a positive and a negative zero?
Yes. In IEEE 754 single precision the sign is bit 31, so zero exists as two bit patterns, usually written +0 and -0. As values they compare equal, so neither can disturb a deadband compare. The distinction only bites in display logic that tracks a sign beside the value: a sign can lag the number through zero and read wrong for a scan. Numeric compares and the absolute-value instruction make the whole question disappear.
Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.