Round REAL Values to N Decimals in Siemens TIA Portal v18

David Krause17 min read
SiemensTIA PortalTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Round REAL Values to N Decimal Places in Siemens TIA Portal v18

When an S7-1200 logs a measured value such as 22.519432 to a CSV via DataLogWrite, the operator typically expects the on-screen value (22.52) to match the file. TIA Portal V18 does not expose a built-in "round to N decimals" instruction that returns a REAL. The ROUND instruction rounds to the nearest DInt and silently drops fractional information, so the engineer must assemble the result from VAL_STRG, arithmetic, or integer scaling. This reference documents every available technique, explains why the HMI shows 22.52 while the CSV shows 22.51999994, and gives the verification steps to prove the chosen method is stable on real hardware.

Applies to: STEP 7 Basic / Professional V18 (TIA Portal V18), S7-1200 CPU firmware V4.2 through V4.6, WinCC Comfort/Advanced V18, WinCC Unified V18. The same techniques apply to S7-1500 and ET 200SP CPUs without code changes other than tag types.

1. Problem Overview

Consider this S7-1200 scenario: an operator presses "Generate Report" on a TP700 Comfort panel. The PLC calls DataLogWrite to append one line of measured REAL data to a CSV stored in the CPU's internal flash. The HMI displays the same value to two decimal places (configured in the output field properties). When the operator opens the CSV in Excel, however, they see 22.51999994 instead of 22.52. The discrepancy has two distinct causes:

  1. Precision drop in the file: DataLogWrite converts the REAL to its full 7-digit ASCII representation, including IEEE 754 noise, before writing. The HMI, by contrast, rounds the value at display time using the configured decimal count.
  2. IEEE 754 representation error: The literal decimal 22.52 has no exact 32-bit binary equivalent. The nearest representable REAL is approximately 22.519999.... Operators see this as the PLC "lying" about the value.

Both issues can be fixed, but the fix depends on whether the data is destined for human display, persistent storage, or downstream Excel analysis. The next sections present three progressively more robust methods, starting with the simplest and ending with the technique recommended for regulated or high-integrity logs.

2. Why the ROUND Instruction Fails for Sub-Integer Precision

TIA Portal V18 ships the floating-point conversion instructions CEIL, FLOOR, ROUND, and TRUNC. All four return a DInt when the input is a REAL, and all four discard the fractional part. They do not accept a "decimals" parameter. A common mistake is to assume that ROUND(x, 2) would round to two decimals; that overload exists in many PC languages and in some PLC dialects, but not in the STEP 7 basic instruction set.

Instruction Input Output Type Effect on 22.519432 Result
CEIL REAL DInt Round toward +∞ 23
FLOOR REAL DInt Round toward -∞ 22
TRUNC REAL DInt Round toward zero 22
ROUND REAL DInt Round to nearest (ties away from zero) 23

None of the four produce 22.52. To retain fractional precision, the engineer must either keep the result as REAL (using arithmetic) or convert to a STRING (using VAL_STRG) before the value reaches the file or the panel.

Tie-breaking note: ROUND in STEP 7 follows the IEEE 754 "round to nearest, ties to even" rule (banker's rounding) for some firmware versions, and "ties away from zero" in others. Always verify on the actual CPU when a measurement can land exactly on a midpoint. If deterministic tie behavior is required, prefer the explicit arithmetic method in Section 5.

3. IEEE 754 Single-Precision Limitations in S7-1200

An S7-1200 REAL occupies 4 bytes and follows the IEEE 754 single-precision format: 1 sign bit, 8 exponent bits, 23 mantissa bits. Many decimal fractions, including 22.52, 1.3, and 0.1, have no exact binary representation, so they are stored as the closest representable approximation. When the PLC performs arithmetic on these approximations, the error compounds.

Three artifacts are common in field logs:

  1. Noise in the low bits: 22.52 is stored as 22.519999... something. Reading the same tag back displays the rounded decimal the HMI applies, not the underlying bit pattern.
  2. Non-zero addends: 0.1 + 0.2 in TIA Portal SCL evaluates to 0.30000000000000004, identical to the Windows result because both use the same IEEE 754 semantics.
  3. Lossy scaling: Dividing a REAL by 3 and multiplying by 3 does not return the original value. The 23-bit mantissa gives roughly 7.22 significant decimal digits; after 4 to 5 multiplications the cumulative error is visible at the 6th decimal.

These artifacts are not bugs in the PLC firmware. They are the consequence of representing a 10-base decimal in a 2-base binary. The fix is to either mask the noise at the boundary (the HMI does this automatically) or eliminate it by storing scaled integers (Section 6).

Watch table tip: Add the measurement tag to a watch table in TIA Portal and change the display format to "Float" with 7 decimal places. You will see the true binary value, which is what DataLogWrite writes. The HMI, with 2 decimal places configured, performs its own rounding at display time, hiding the noise.

4. Method 1: VAL_STRG Conversion (Recommended for HMI and DataLog)

VAL_STRG is a basic instruction in TIA Portal that converts a numeric value to a STRING with explicit control over precision, sign handling, and thousands separator. Passing the resulting STRING to DataLogWrite ensures the CSV contains exactly the digits you specified, no more, no less.

4.1 VAL_STRG Parameter Table

Parameter Direction Type Meaning Typical Value
IN Input REAL / LREAL / DInt / Int Source value 22.519432
FORMAT Input WORD Output format flags (decimal, hex, octal, BCD, sign handling) 16#0000 for signed decimal fixed-point
PRECISION Input BYTE / USInt Number of decimal places to round to (0 to 7 for REAL, 0 to 15 for LREAL) 2
SEPARATOR Input BOOL / DInt Thousands separator behavior FALSE / 0
STR Output STRING / WSTRING Result string buffer strValue : STRING[20]
RET_VAL Output INT Status; 0 = no error, non-zero = string too short Check ENO

4.2 SCL Example (TIA Portal V18)

// Round a REAL to 2 decimals and write to STRING for DataLogWrite
// Call from OB1 or a dedicated FB on every measurement cycle

"dbLog".strTemp := '';  // optional clear

VAL_STRG(
    IN        := "dbLog".rMeasuredValue,    // REAL: 22.519432
    FORMAT    := 16#0000,                   // signed decimal, no hex, no thousands
    PRECISION := 2,                         // 2 decimal places
    SEPARATOR := FALSE,                     // no thousands separator
    STR       := "dbLog".strTemp,          // STRING[20] buffer
    RET_VAL   => "dbLog".iStrgErr          // 0 on success, <>0 if STR too short
);

IF "dbLog".iStrgErr = 0 THEN
    // Pass the STRING (not the REAL) to DataLogWrite
    "DataLogWrite_DB"(REQ := TRUE,
                      ID  := "dbLog".hLogID,
                      values := "dbLog".strTemp,
                      done => "dbLog".bWriteDone,
                      error => "dbLog".wWriteErr);
END_IF;

The STR buffer must be large enough to hold the result plus the STRING header (two length bytes plus the characters). For a 32-bit REAL with sign, integer part up to 9 digits, decimal point, and 7 decimals, the maximum output is 18 characters. Declare STRING[20] to keep one byte of headroom. If RET_VAL returns a non-zero value (typically 16#0007 for "string too short"), increase the buffer size.

4.3 SCL Variant Using WSTRING for WinCC Unified

WinCC Unified panels and Unified Comfort Panels expect Unicode strings. Declare the buffer as WSTRING and use the matching conversion instruction:

VAL_STRG(
    IN        := "dbLog".rMeasuredValue,
    FORMAT    := 16#0000,
    PRECISION := 2,
    SEPARATOR := FALSE,
    STR       := "dbLog".wstrTemp,        // WSTRING[20]
    RET_VAL   => "dbLog".iStrgErr
);

5. Method 2: Arithmetic Rounding (Multiply, Round, Divide)

If the application must keep the value as a REAL for further arithmetic (for example, summing, integrating, or PID control), use the classic two-step: scale up, round to integer, scale back down. The PLC-native version of the expression floor(x * 100 + 0.5) / 100 uses REAL_TO_DINT for the integer conversion and DINT_TO_REAL for the division.

// rInput  : REAL  (e.g. 22.519432)
// rOutput : REAL  (e.g. 22.52, but stored as the nearest REAL)

"dbMath".rScaled := "dbMath".rInput * 100.0;            // 2251.9432
"dbMath".diRound := REAL_TO_DINT("dbMath".rScaled + 0.5); // 2252
"dbMath".rOutput := DINT_TO_REAL("dbMath".diRound) / 100.0; // 22.52

This expression is idiomatic across many languages; the form floor(x * 100 + 0.5) / 100 appears in vendor documentation such as the HPE expression-language reference as a general-purpose two-decimal rule. In TIA Portal SCL, the explicit REAL_TO_DINT cast is preferred because it does not rely on implicit conversions that some firmware versions may warn about.

5.1 Caveats

  • Tie behavior is configurable. REAL_TO_DINT truncates toward zero. Adding 0.5 first produces "round half up" for positive numbers and "round half toward zero" for negative numbers. For symmetric half-away-from-zero behavior, branch on the sign bit.
  • The output is still a REAL. The value 22.52 is stored as the nearest binary float, not as the literal decimal. Pass the result to VAL_STRG if you need the file to contain 22.52 rather than 22.519999....
  • Overflow. If |rInput| > 2.147e7 the intermediate DInt will overflow. Pre-check the range, or use LREAL (LRealToDInt variant) and accept the 53-bit mantissa range.

6. Method 3: Integer Scaling (Store as DInt with Implied Decimals)

The most robust approach for persistent logging, regulatory reporting, and downstream analytics is to abandon REAL for the stored value altogether. Multiply the measurement by 10^N and store the result as a DInt (or LReal if the range requires). The decimal point is implied by the program convention ("this tag is in hundredths").

// rInput  : REAL  (e.g. 22.519432)
// diCenti : DInt  (e.g. 2252, representing 22.52)
// To display: divide by 100.0 only at the HMI or in the CSV writer

"dbStorage".diCenti := REAL_TO_DINT("dbStorage".rInput * 100.0 + 0.5);

// For display only:
"dbDisplay".rShown := DINT_TO_REAL("dbStorage".diCenti) / 100.0;

Advantages:

  • The integer 2252 is lossless. Re-scaling to REAL for display still produces a binary approximation, but the underlying measurement is captured exactly (within the original measurement precision).
  • Excel analysis is simpler. The CSV column reads 2252 instead of 22.51999994. A header note such as Value (x 0.01) documents the implied scaling.
  • No string conversion is required for DataLogWrite. The DInt writes cleanly without VAL_STRG.
  • PID blocks, totalizers, and average calculations operate on stable integer values, eliminating cumulative rounding error from running sums.
Convention discipline: Use a consistent unit naming convention in the tag table: rFlow_Lmin for the raw REAL, diFlow_cLmin for the centi-unit DInt. This avoids the kind of off-by-100 incidents that have caused 100x scale bugs in process logs.

7. DataLogWrite and CSV File Generation on S7-1200

The S7-1200 DataLog instruction set comprises FBs DataLogCreate (FB 80), DataLogOpen (FB 81), DataLogWrite (FB 82), DataLogClose (FB 83), DataLogNewFile (FB 84), DataLogClear (FB 85), and DataLogDelete (FB 86). When you pass a REAL to DataLogWrite, the firmware calls an internal float-to-ASCII routine that emits the full 7-digit IEEE 754 representation. When you pass a STRING, the firmware writes the buffer characters verbatim.

Input Type to DataLogWrite CSV Content for 22.519432 Operator Interpretation
REAL 22.519999... (7 digits) Confusing, looks like a different value
STRING (from VAL_STRG, PRECISION=2) 22.52 Matches the HMI
DInt (scaled x 100) 2252 Matches HMI, requires header note

7.1 DataLogCreate Configuration Tips

  1. Open DataLogCreate and define one column per measured variable. For STRING columns, set the maximum length to match the VAL_STRG output buffer (e.g. 20 characters).
  2. Specify the storage location: Internal flash (load memory, ~2 MB on a typical S7-1200 CPU), SIMATIC Memory Card (SMC), or WebAPI/UserFiles (S7-1500 only).
  3. Set the HEADER parameter to a comma-separated list of column names; this becomes the first row of the CSV.
  4. Always evaluate STATUS from every DataLog call. Common error codes include 16#0001 (no memory), 16#0002 (file exists when CREATE), and 16#000A (file system full).

8. S7-1200 Internal Flash Memory Write Cycle Constraints

Internal load memory and SIMATIC Memory Cards both use NAND flash, which has a finite number of write/erase cycles per sector. The official Siemens support article "SMC - Service life of SIMATIC memory cards" quotes a typical figure of 500,000 write cycles for SMC cards. The internal flash on S7-1200 CPUs uses a similar NAND technology and should be treated as having the same endurance class, though the actual limit is firmware-dependent and not always published in the datasheet.

Worked example: a report-generation cycle that writes one CSV row every minute, 24/7:

Writes per year = 60 min/h * 24 h/day * 365 day/year = 525,600
Endurance        = 500,000 cycles
Service life     = 500,000 / 525,600 = 0.95 years (~347 days)

For a write-on-demand scenario ("operator presses Generate Report"), the 500,000 cycle limit corresponds to roughly 1,370 years of daily reports and is not a practical concern. For continuous logging, however, the limit becomes binding in well under a year. Mitigation strategies include:

  • Buffer in work memory and flush less often. Accumulate rows in a STRING array, then write a batch every N minutes.
  • Rotate to a new file. DataLogNewFile opens a fresh CSV; old files stay on the SMC but new writes hit a new sector.
  • Use a removable SMC. The SMC can be replaced as part of preventive maintenance without changing the program.
  • Offload to a network share. S7-1200 supports FTP (server) from firmware V4.1 onward; write to a NAS instead of the local flash.
Wear-leveling awareness: The S7-1200 firmware uses a basic wear-leveling algorithm that distributes writes across unused sectors, but the algorithm cannot rescue an application that hammers a single DataLog file continuously. Treat the endurance limit as a hard constraint, not a guideline.

9. HMI Display Consistency with WinCC

The HMI can be configured to display any number of decimal places, independent of the underlying tag format. This is why the operator sees 22.52 on screen but 22.51999994 in the file: the HMI does the rounding, the file does not.

9.1 WinCC Comfort / Advanced Output Field

  1. Select the output field bound to the measurement tag.
  2. Open Properties > Appearance > Format.
  3. Set Display format to 999.99 (2 decimals) or use the "Decimal places" property.
  4. Confirm the field binding is to the REAL tag, not to a derived STRING. If the binding is to the STRING from Section 4, the HMI will display the string verbatim (no extra rounding needed).

9.2 WinCC Unified Output Field

  1. Open the screen and select the IO field.
  2. In Properties > General, set Format to {N2} for 2 decimals.
  3. For higher precision values, use {N4}, {N6}, etc.
Display format vs. data format: The WinCC format pattern only affects on-screen rendering. The internal tag value is unchanged. The CSV inherits the internal value, which is why the two diverge unless the PLC applies the rounding before writing.

10. Verification and Validation Procedure

Follow this checklist on the bench before deploying to production:

  1. Watch-table check. Add the source REAL tag and the output STRING/DInt tag to a watch table. Force the source to known values (0.0, 22.519432, -3.14159, 9.99999) and confirm the output is correct for each.
  2. Tie-break check. Force the source to a value that lands exactly on a midpoint, e.g. 22.505000 (rounds to 22.51 with round-half-up, or 22.50 with banker's rounding). Confirm the chosen method matches the documented behavior.
  3. Range check. Force the source to the maximum and minimum expected process values, plus a 10% overrange. Confirm no overflow in the intermediate DInt or the STRING buffer.
  4. DataLog round-trip. Trigger DataLogWrite for each test value. Extract the CSV from the SMC (or internal flash via WebAPI on S7-1500). Open in Excel and verify the column matches the expected rounded output.
  5. HMI cross-check. View the same tag on the panel. Confirm the on-screen value matches the CSV value character-for-character.
  6. Endurance smoke test. For continuous-logging applications, run 10,000 write cycles in a loop and inspect the SMC error count via RDSYSST or by reading the diagnostic buffer. Any non-zero STATUS from the DataLog FBs indicates the wear limit is being approached.

11. Troubleshooting Matrix

Symptom Likely Cause Fix
CSV shows 22.519999, HMI shows 22.52 REAL passed directly to DataLogWrite Pass VAL_STRG output (Method 1) or scaled DInt (Method 3)
CSV shows "########" or empty cell STRING buffer too small for VAL_STRG output Increase STRING[N] declaration to 20+ characters
VAL_STRG RET_VAL returns 16#0007 String too short error Resize buffer, or reduce PRECISION
Rounded value is off by 0.01 on ties (e.g. 22.505 → 22.50 instead of 22.51) Banker's rounding in REAL_TO_DINT Branch on sign and add 0.5 manually for positive values, subtract 0.5 for negative
DIInt overflow during scaling Input exceeds ±2.147e7 / 10^N Use LREAL intermediate, or rescale to a smaller unit (e.g. milli- to deci-)
DataLogWrite STATUS = 16#0001 after months of operation Flash memory write cycle exhaustion Reduce write frequency, rotate files, replace SMC, or offload to FTP
HMI shows 22.5199 instead of 22.52 Output field format pattern not set to 2 decimals Set "Decimal places" property in WinCC field configuration
CSV column contains commas inside the value VAL_STRG used thousands separator Set SEPARATOR parameter to FALSE / 0
Negative values lose sign in CSV FORMAT word set to unsigned decimal Set FORMAT to 16#0000 (signed) or 16#0001 (signed, with leading space)
ROUND in SCL behaves differently from ROUND in LAD/FBD Implicit type conversion in SCL Use explicit REAL_TO_DINT with manual offset for tie control

12. Method Selection Cheat Sheet

Scenario Best Method Reason
Display only, no logging Configure HMI decimal places (Section 9) PLC stays in REAL, no extra code
Operator-triggered CSV report, HMI + file must match VAL_STRG + STRING to DataLogWrite (Section 4) Exact character match between screen and file
Continuous high-rate logging, Excel analysis downstream Scaled DInt to DataLogWrite (Section 6) Lossless, no string overhead, simple header note
Further math on the rounded value (sum, average, PID) Arithmetic rounding, keep as REAL (Section 5) Avoids repeated string conversion in hot path
Regulated / auditable data capture Scaled DInt (Section 6) + signed audit tag IEEE 754 noise eliminated from stored value
Final recommendation: For the S7-1200 + WinCC Comfort scenario described in the original question, route every measured REAL through VAL_STRG with PRECISION = 2 into a STRING[20] buffer, bind the HMI output field to that same string buffer, and pass the buffer to DataLogWrite. The HMI shows 22.52, the CSV shows 22.52, and the underlying REAL remains untouched for any further arithmetic. This is the lowest-risk path with the smallest memory footprint.

FAQ

Can the ROUND instruction in TIA Portal V18 round to 2 decimal places?

No. The ROUND, CEIL, FLOOR, and TRUNC instructions in STEP 7 Basic/Professional V18 all return a DInt and discard the fractional part. To round to N decimal places, use VAL_STRG with PRECISION = N (string output) or apply the expression DINT_TO_REAL(REAL_TO_DINT(x * 10^N + 0.5)) / 10^N (REAL output).

Why does the CSV show 22.51999994 while the HMI shows 22.52 for the same tag?

Because DataLogWrite writes the full 7-digit IEEE 754 representation of the REAL (22.51999994 is the nearest 32-bit float to 22.52), while the HMI output field rounds at display time using the configured "Decimal places" property. The two diverge unless the PLC rounds before writing, either by passing a STRING from VAL_STRG (with PRECISION = 2) or by writing a scaled DInt.

How many write cycles does the S7-1200 internal flash support before failing?

The SIMATIC Memory Card service life is documented at approximately 500,000 write cycles per sector in Siemens support article entry ID 109482591. The internal load memory of an S7-1200 CPU uses comparable NAND technology and should be assumed to have similar endurance. A write every minute, 24/7, exhausts 500,000 cycles in roughly 347 days; a write once per operator action is typically not a concern.

Should I store measured values as REAL or as a scaled DInt in TIA Portal V18?

For values that will be displayed only, REAL is fine and lets the HMI handle the formatting. For values that will be logged, summed, or analyzed in Excel, a scaled DInt (e.g. REAL_TO_DINT(x * 100 + 0.5) with implied 2 decimal places) is more robust: it eliminates IEEE 754 noise from the stored record, simplifies CSV parsing, and produces lossless round-trips when the value is re-scaled for display.

What is the difference between VAL_STRG with FORMAT 16#0000 and FORMAT 16#0001?

FORMAT = 16#0000 produces a signed decimal with no leading space (e.g. -22.52). FORMAT = 16#0001 reserves one character for a sign placeholder, adding a leading space for positive values so that positive and negative numbers right-align in a fixed-width column (e.g. 22.52 vs -22.52). For CSV output where alignment does not matter, 16#0000 is the standard choice.

Does rounding a REAL to 2 decimals and storing as REAL remove the IEEE 754 noise?

No. The arithmetic rounding method (multiply, round to DInt, divide) produces a REAL value that is the binary representation of the rounded decimal, which still has IEEE 754 noise. The display will be correct (22.52) but a watch table with 7 decimal places will still show 22.519999... For a noise-free stored value, convert the rounded DInt to a DInt and store that, only converting back to REAL at the display or logging boundary.

Back to blog