Overview: The IEEE 754 Reality Behind a Siemens REAL
In Siemens SIMATIC S7-300, S7-400, S7-1200, and S7-1500 CPUs, every variable declared as REAL occupies 32 bits of data memory and is stored in IEEE 754 single-precision binary floating-point format. This is the same representation used by virtually every PC, PLC, and embedded controller built since the 1990s. Because the format stores values in base 2, most decimal fractions cannot be represented exactly. The STEP 7 or TIA Portal online monitor rounds the displayed value to a fixed number of digits (typically 6 or 7 significant digits) to keep the screen readable, but the underlying 32-bit word contains a binary value that almost never matches a clean decimal number.
A practical example from STEP 7 shows this clearly. The user enters or computes the decimal value 0.14, but the monitor displays it as 0.1400000 while the actual bit pattern in the data block decodes to the IEEE 754 single-precision value 0.14000000059604644775390625. The difference is on the order of 6×10⁻¹⁰, well below the resolution of a 6-digit display, but it is the real value the CPU will use in any subsequent arithmetic, comparison, or conversion.
When a control program needs to round or shorten a REAL to a fixed number of decimal places — for example, 0.1234567 to 0.12 or 0.13 — the engineer must apply a deliberate rounding algorithm rather than rely on display formatting. Display formatting only changes how the value is rendered; it does not change the value the CPU computes with.
Why 0.1400000 Is Not 0.14: Binary Fraction Limitations
The IEEE 754 single-precision format dedicates 1 bit to the sign, 8 bits to an excess-127 exponent, and 23 bits to a normalized mantissa. Any finite, non-subnormal value is stored as:
value = (-1)^sign × 1.mantissa₂ × 2^(exponent - 127)
The only decimal fractions that have a finite binary expansion are sums of negative powers of two: 0.5, 0.25, 0.75, 0.625, 0.875, 0.125, and so on. Every other terminating decimal — including 0.1, 0.2, 0.3, 0.14, 0.12, 0.13 — has an infinite repeating binary expansion. The 23-bit mantissa truncates that infinite expansion, producing a value that is close to, but not equal to, the decimal literal the programmer typed.
This is not a Siemens bug. The same phenomenon is documented in the Python language reference (which also uses IEEE 754 doubles internally) and in every general-purpose numerical analysis textbook.
Python 3 documentation: Floating-Point Arithmetic: Issues and Limitations
For an S7 application the consequence is twofold:
- The actual binary value of any decimal REAL always carries a small error, typically smaller than 2⁻²³ ≈ 1.19×10⁻⁷.
- A rounding algorithm that multiplies by a power of 10, rounds, and divides back amplifies the error. The result will sometimes be exactly the desired decimal (for example, 0.12) and sometimes land on the nearest representable neighbor (for example, 0.11999999 or 0.12000004). Both outcomes are still valid IEEE 754 numbers; neither is wrong, but the engineer must decide which form to accept.
For an in-depth treatment of mantissa shortening as a numerical algorithm, see Number shortening algorithms (ScienceDirect) — the canonical academic reference on rounding and truncation of floating-point mantissas.
Prerequisites
To implement the techniques in this article you need:
- A SIMATIC S7-300, S7-400, S7-1200, or S7-1500 CPU. The algorithms work identically on all four families because all four store REAL as IEEE 754 single precision.
- STEP 7 V5.x (for STL/SCL on S7-300/400) or STEP 7 V13 SP1 or later in TIA Portal (for SCL on S7-1200/1500). The TIA Portal STL editor is enabled for S7-1500 from V16 onwards; on S7-1200 the editor is read-only for legacy STL code imported from older projects.
- A function block, data block, or tag table in which to declare the input REAL, the output REAL, and any temporaries.
| Item | Minimum version | Notes |
|---|---|---|
| STEP 7 V5.5 SP2 | S7-300/400 STL/SCL | Required for the RND and DTR instructions used in the STL algorithm |
| TIA Portal V13 SP1 | S7-1200/1500 SCL | First release with full SCL support for S7-1500 |
| TIA Portal V16 | S7-1500 STL | STL editor enabled with S7-1500 target |
| S7-1500 CPU firmware | ≥ V2.0 for STL execution | Earlier firmware rejects STL as an unknown language |
| S7-1200 CPU firmware | ≥ V4.0 | All S7-1200 firmware releases support SCL fully |
Siemens documentation for TIA Portal, STEP 7 V5.x, and the S7-1500 system manual is available from the Siemens Industry Online Support portal.
Truncation vs Rounding: Choosing the Right Operation
Before writing any code, the engineer must decide whether the application requires truncation or mathematical rounding.
- Truncation discards the digits beyond a chosen position without considering their value. 0.1234567 truncated to two decimal places is 0.12; 0.1299999 truncated to two places is also 0.12, not 0.13.
- Mathematical (nearest) rounding looks at the digit immediately after the chosen position. If that digit is 5 or higher, the value is rounded up; otherwise it is rounded down. 0.1234567 rounded to two places is 0.12; 0.1299999 rounded to two places is 0.13. Siemens' RND instruction implements IEEE 754 round-to-nearest-even, also called banker's rounding, which is the IEEE 754 default. 0.125 therefore rounds to 0.12, not 0.13, because 0.12 is the nearest even integer multiple of 0.01.
For most process-control displays (temperature, pressure, level) the engineer wants banker's rounding. For human-readable values some users prefer round-half-away-from-zero, which is not directly available as a single instruction. In that case, add a small bias (for example, 0.5 × 10⁻ⁿ) before applying RND, with sign-conditional handling for negative numbers.
STL Implementation in STEP 7 V5.x
The classic Statement List algorithm multiplies by a power of 10, rounds to DINT, converts back to REAL, and divides by the same power. This is the same technique used in many pre-IEEE-754 controllers and is the most portable form for S7-300/400.
// Input : #value (REAL)
// Output: #output (REAL), rounded to #Decimals places
// Factor: 10.0^#Decimals
L #value // Load input REAL
L 100.0 // 10.0 for 1 decimal, 100.0 for 2, 1000.0 for 3, ...
*R // value * factor, result in ACCU1
RND // Round ACCU1 to nearest DINT (round-to-nearest-even)
DTR // Convert DINT to REAL
L 100.0 // Same factor as above
/R // Divide by factor
T #output // Store rounded REAL
The RND instruction operates on the floating-point value in ACCU1 and produces a 32-bit double integer in ACCU1, setting the status bits OV and OS on overflow. DTR then converts that DINT to a REAL so it can be divided by the original factor.
For one decimal place, replace both 100.0 literals with 10.0. For three decimal places, replace both with 1000.0. The factor must match on both sides; if they differ, the result is silently scaled wrong.
The algorithm produces a REAL, so the result is still subject to IEEE 754 representation. The most common side effect is that the rounded value sometimes appears as 0.11999999 or 0.12000004 in the online monitor rather than the exact 0.12. This is normal and does not indicate a bug.
SCL Implementation in STEP 7 V5.x and TIA Portal
SCL is a Pascal-like high-level language available in both STEP 7 V5.x and TIA Portal. It is easier to read, parameterize, and maintain than STL.
FUNCTION "RoundReal" : Real
VAR_INPUT
Value : Real;
Decimals: Int;
END_VAR
VAR_TEMP
Factor : Real;
Scaled : Real;
AsInt : DInt;
END_VAR
BEGIN
Factor := INT_TO_REAL(EXPD(10, Decimals)); // 10^Decimals
Scaled := Value * Factor;
AsInt := REAL_TO_DINT(Scaled); // rounds to nearest
"RoundReal" := DINT_TO_REAL(AsInt) / Factor;
END_FUNCTION
EXPD(10, Decimals) raises 10 to the power Decimals. EXPD is available in both STEP 7 V5.x SCL and TIA Portal SCL and returns a REAL. The IEC 61131-3 standard conversion functions REAL_TO_DINT and DINT_TO_REAL are part of the standard library and resolve to the same machine-code sequence as RND and DTR.
The behavior is identical to the STL algorithm. The SCL compiler will, in most cases, emit the same RND/DTR/divide sequence in machine code, so there is no performance penalty.
For a round-half-away-from-zero variant, bias the scaled value by half a ULP before the conversion:
Scaled := Value * Factor;
IF Scaled >= 0.0 THEN
AsInt := REAL_TO_DINT(Scaled + 0.5);
ELSE
AsInt := -REAL_TO_DINT(-Scaled + 0.5);
END_IF;
TIA Portal Implementation on S7-1200 and S7-1500
TIA Portal provides a built-in instruction called ROUND (and CEIL, FLOOR, TRUNC) that can be inserted from the Instructions task card under Basic instructions → Math functions. Unlike the legacy STL RND, the TIA ROUND block accepts a Real input and returns a DInt, performing the same round-to-nearest-even. It is typically used inside a function block:
FUNCTION_BLOCK "FbRoundReal"
VAR_INPUT
Value : Real;
Decimals : Int;
END_VAR
VAR_OUTPUT
OutValue : Real;
END_VAR
VAR
Rounded : DInt;
Factor : Real;
END_VAR
BEGIN
Factor := INT_TO_REAL(EXPD(10, Decimals));
Rounded := REAL_TO_DINT(Value * Factor + 0.5);
OutValue := DINT_TO_REAL(Rounded) / Factor;
END_FUNCTION_BLOCK
The +0.5 bias converts round-half-to-even into round-half-away-from-zero, which matches the intuition most operators have when looking at an HMI value. For a wrapper block that exposes both modes, add a Boolean input "RoundHalfUp" that selects between the biased and unbiased variants.
For a reusable multi-decimal variant, the same factor technique as the STL/SCL version applies; only the literals change. Wrap the routine in a function block and pass Decimals as an input parameter so that one instance can serve several displays.
Edge Cases and Verification
Three classes of edge cases must be tested in any production deployment of a REAL-rounding routine.
1. Display mismatch. The most frequent complaint is that a value entered as 0.12 displays as 0.11999999 or 0.12000004. This is correct IEEE 754 behavior and cannot be eliminated without converting to a string for display. If the HMI accepts strings, format the REAL with a fixed number of decimal places there; the PLC should still keep the rounded REAL for arithmetic.
2. Overflow at the RND instruction. RND and REAL_TO_DINT set OV if the scaled value falls outside the DInt range (-2 147 483 648 to 2 147 483 647). For three decimal places, the input REAL must therefore be within roughly ±2 147 483.647. For two decimal places the range is ±21 474 836.47. For one decimal place it is ±214 748 364.7. Most process variables are far smaller, but if the routine is used on a high-magnitude engineering value (for example, kilowatt-hours on a feeder) the engineer must check the worst case.
3. NaN and Inf propagation. If the input is NaN (for example, the result of 0.0 / 0.0) or +Inf / -Inf, the multiplication and division propagate the special value unchanged, but RND and REAL_TO_DINT set OV and return an undefined DInt. Add a check before the conversion:
IF NOT IS_VALID_REAL(Value) THEN
OutValue := 0.0;
RETURN;
END_IF;
IS_VALID_REAL is available in TIA Portal SCL V14 and later; for STEP 7 V5.x, test against 0.0/0.0 separately or read the OK output of the legacy ROUND function block.
4. Negative numbers. Both RND and REAL_TO_DINT round to nearest, with ties-to-even, regardless of sign. -0.125 therefore rounds to -0.12, not to -0.13. The +0.5 bias trick works only for positive values; for negatives subtract 0.5 (or use the sign-conditional pattern shown earlier).
5. Subnormal and denormal inputs. REAL values smaller than 1.4×10⁻⁴⁵ are denormalized on S7-1500 CPUs; they are rounded correctly by the same algorithm but at a performance cost of roughly 5× to 10×. For inputs that can drop to subnormal levels (for example, integration of a slow sensor), scale the input by a fixed factor before the routine and scale it back after.
A useful verification pattern is to write a small test FC that loops over a known set of inputs and outputs, checks the result against an expected array, and accumulates a mismatch counter. The counter can be exposed on the HMI for acceptance-test screens.
| Input REAL | Expected (2 dp) | RND result (REAL) | Hex display (approx) | Pass? |
|---|---|---|---|---|
| 0.1234567 | 0.12 | 0.11999999 | 0x3DF5C28F | Yes (within 1 ULP) |
| 0.1250000 | 0.12 | 0.12000000 | 0x3DF5C28F | Yes (banker's) |
| 0.1350000 | 0.14 | 0.13000000 or 0.14000000 | depends on mantissa | Inspect |
| 12345.6789 | 12345.68 | 12345.6789 | 0x4640E5C0 | Yes |
| -0.1250000 | -0.12 | -0.12000000 | 0xBDF5C28F | Yes (banker's) |
| 1.0E+10 | 10000000000.00 | overflow at RND | OV set | No — guard with range check |
Commissioning and Diagnostics
When commissioning the routine, follow this sequence:
- Offline test in PLCSIM. Create a watch table that drives the input REAL with a sweep of edge values (0, smallest positive subnormal, 0.125, 0.135, max safe value, -0.125, NaN, +Inf). Observe the output and the OV bit.
- Online monitor with a known stimulus. Force a tag in the data block to a value the online monitor does not pre-round (for example, 0.1234567) and confirm the output matches the expected 0.12 within one ULP.
-
HMI sanity check. If the HMI shows 0.11999999 when the operator expects 0.12, add a string-format routine on the HMI side. Most Siemens Comfort Panels and WinCC Runtime accept a fixed-decimal format string such as
0.00that suppresses the trailing digits. - Long-running drift test. If the rounded value feeds a counter or integrator, run the loop for at least one full process cycle and confirm the integrated value matches the closed-form expectation to within the same ULP tolerance.
The status word bits of interest are:
- OV (bit 3, BR overflow) — set by RND / REAL_TO_DINT on out-of-range input
- OS (bit 4, stored overflow) — latched OV, cleared by the next JOS instruction
- CC 1, CC 0 — set to 0,0 on RND success, 0,1 on overflow
Read these in the watch table or evaluate them with the legacy STW view in STEP 7.
Performance Considerations
Each call to the rounding routine costs:
- One MULR (or
*R) on REAL - One RND (or REAL_TO_DINT)
- One DTR (or DINT_TO_REAL)
- One DIVR (or
/R) on REAL
| CPU | Approx. execution time | Notes |
|---|---|---|
| S7-1500 CPU 1511-1 PN | 200 ns to 400 ns | Optimized bit-fused FP path |
| S7-1500 CPU 1518-4 PN/DP | ~100 ns | Hardware FP unit |
| S7-1200 CPU 1214C | 1.5 µs to 2.5 µs | Software FP |
| S7-300 CPU 315-2 PN/DP | 4 µs to 6 µs | Software FP |
| S7-400 CPU 416-3 PN/DP | ~1 µs | Hardware FP coprocessor on 41x series |
For HMI display updates that fire every 100 ms or 250 ms, the cost is irrelevant. For high-speed closed-loop control (cycle times of 1 ms or less), call the routine only on the display update OB, not in the servo OB. Alternatively, perform the rounding only when the value crosses a deadband:
IF ABS(Value - LastRounded) > 0.005 THEN
LastRounded := RoundReal(Value, 2);
END_IF;
This reduces the call rate by an order of magnitude on slow-moving signals and prevents unnecessary HMI flicker.
Platform Differences: S7-300/400 vs S7-1200/1500
Although the algorithm is identical, a few platform differences affect deployment.
| Aspect | S7-300/400 | S7-1200/1500 |
|---|---|---|
| REAL storage | IEEE 754 single precision | IEEE 754 single precision |
| LREAL availability | Limited on S7-300, full on S7-400 | Full on both |
| STL availability | Native | S7-1500: V2.0+; S7-1200: read-only |
| SCL availability | Yes (optional package) | Yes (built-in) |
| ROUND instruction | STL RND only | STL RND, TIA ROUND block, IEC REAL_TO_DINT |
| Math precision library | Limited | Full IEC 61131-3 math library in TIA Portal |
| Optimizer behavior | No SCL optimizer | SCL optimizer inlines small functions and removes dead temporaries |
If the application must run on both S7-300/400 and S7-1200/1500, prefer the SCL form because it compiles on both platforms without modification. The STL form is restricted to CPU families that support Statement List, which excludes most S7-1200 projects.
Exporting Rounded Values Over OPC UA and Profinet
When the rounded REAL is exported over OPC UA, Profinet IO, or S7 communication, the same IEEE 754 representation travels on the wire. Siemens' OPC UA server on S7-1500 (firmware V2.0 with the OPC UA option) exposes REAL as a 32-bit single-precision float with the standard OPC UA data type Float (NodeId ns=0;i=10). Round-trip integrity is guaranteed because the receiver decodes the same 32 bits, but the receiver may render the value with a different display format. Configure the OPC UA companion specification on the receiver to use a fixed-decimal display mask of 0.00, 0.000, or whatever the application requires.
For Profinet IO, the REAL is transported in the IO data image as 4 bytes, big-endian per the Profinet specification. No conversion is performed, so the same .999999 / .000001 artifacts appear on the receiver if the receiver also displays the value as a raw float. Configure the IO controller's HMI or SCADA to format on read, not on write.
Alternatives: String Formatting
In cases where the rounded value is used only for display and never for further arithmetic, the cleanest solution is to skip the REAL-to-REAL rounding and instead convert the REAL to a formatted string with a fixed number of decimal places. The string can be generated either on the PLC side using the SCL VAL_STRG or SCONV instructions, or on the HMI side using a format string.
In SCL on an S7-1500 with firmware V2.0 or later:
DisplayString := VAL_STRING(Value, '0.00'); // '0.00' formats to 2 decimal places
VAL_STRING is part of the IEC 61131-3 standard library in TIA Portal V14 SP1 and later. The HMI then displays DisplayString verbatim, so 0.12 prints as 0.12 and not as 0.11999999.
The trade-off is that the string cannot be re-used in arithmetic. If the downstream process needs the rounded number for further computation — for example, to add to an accumulator that is itself rounded — keep the REAL and accept the ULP-level noise.
Python 3 Floating-Point Tutorial — useful general background on why 0.1 + 0.2 ≠ 0.3 in any IEEE 754 system, including Siemens S7.
FAQ
Why does my REAL show 0.1400000 in the online monitor but compute as 0.14000000059604644775390625 in the program?
Siemens stores REAL as IEEE 754 single precision, which uses a binary mantissa. The decimal 0.14 has an infinite binary expansion; the 32-bit mantissa truncates it to the closest representable value, which is exactly 0.14000000059604644775390625. The monitor rounds the display to six or seven digits to keep the table readable.
Which instruction rounds a REAL to a fixed number of decimal places in STEP 7?
In STL, multiply the REAL by 10ⁿ (for example, 100.0 for two decimal places), execute RND to round to a DINT, execute DTR to convert back to REAL, then divide by the same 10ⁿ. In SCL, write the same sequence using REAL_TO_DINT, DINT_TO_REAL, and a factor of INT_TO_REAL(EXPD(10, n)). The TIA Portal ROUND block performs the same conversion for a single power of ten per call.
Does Siemens' RND instruction use round-half-up or round-half-to-even?
RND uses IEEE 754 round-to-nearest-even (banker's rounding). The value 0.125 therefore rounds to 0.12, not to 0.13, because 0.12 is the nearest even multiple of 0.01. To get round-half-away-from-zero behavior, add 0.5 × 10⁻ⁿ to the scaled value before calling RND, with sign-conditional handling for negative numbers.
My rounded REAL displays as 0.11999999 instead of 0.12 on the HMI. How do I fix that?
This is IEEE 754 representation noise; the stored REAL is the closest representable value to 0.12. The cleanest fix is to format the value as a string on either the PLC (SCL VAL_STRING with a '0.00' mask) or the HMI (WinCC or Comfort Panel numeric field with two decimal places) rather than rely on the PLC to produce an exact decimal REAL.
How do I round a REAL that may be NaN or Infinity without crashing the CPU?
Test the value with IS_VALID_REAL (TIA Portal SCL V14+) before calling RND or REAL_TO_DINT, and substitute a defined value (for example, 0.0) on invalid input. RND and REAL_TO_DINT set the OV status bit on NaN, Inf, and out-of-range inputs, so reading the status word is an alternative check in STL.
Why does my rounded REAL show 0.999999 or 0.000001 artifacts on the HMI?
After the multiply-round-divide sequence, the result is the nearest IEEE 754 single-precision value to the rounded decimal. For 0.12 that nearest value is either 0.11999999 or 0.12000004 depending on the mantissa. This is correct behavior; switch the display to a fixed-decimal format string to mask the artifact, or convert the value to a string with VAL_STRING.
Can the same rounding FB run on S7-300/400 STL and S7-1500 SCL without changes?
No. STL runs only on CPU families that support Statement List. SCL is the cross-platform choice; write the rounding routine in SCL and it will compile on S7-300/400 (with the optional SCL package), S7-1200, and S7-1500 with no code changes.