Overview
The STEP 7 v5.5 standard instruction TRUNC returns an unexpected value of 1,333,788,671 when a programmer attempts to convert the REAL constant 4,294,967,295.3 to a DINT. The result is not arithmetic garbage; it is a deterministic consequence of two coupled limits: the 32-bit DINT signed range, and the 24-bit mantissa precision of the 32-bit IEEE-754 REAL. The 4,294,967,295.3 value is outside the DINT range, and the value 4,294,967,295 cannot even be represented exactly as a 32-bit REAL.
This article dissects the bit-level encoding of the result, clarifies the difference between DINT, UDINT, DWORD, and LREAL on the S7-300/400 and S7-1200/1500 platforms, and documents every field-proven workaround for handling 32-bit unsigned integers and 64-bit floating-point values in legacy STEP 7 v5.5 projects.
Problem Description
Symptom: A ladder or FBD network using TRUNC (or ROUND) returns 1333788671 when the REAL input is set to 4,294,967,295.3.
Expected: 4,294,967,295 (the integer part of the constant).
Actual online value of MD / OUT: DW#16#4F7FFFFF interpreted by the PLC as signed DINT = +1,333,788,671.
Trigger values: Any REAL whose magnitude is > 2,147,483,647.0 (the positive DINT limit) or whose integer part is > 16,777,216 (the 24-bit mantissa boundary where single-precision REAL begins to lose 1-LSB accuracy).
Root Cause Analysis
Two independent constraints collide. Either one is sufficient to corrupt the result; the value 4,294,967,295.3 violates both.
Constraint 1: DINT Is a Signed 32-Bit Integer
Per the IEC 61131-3 data-type specification used by Siemens, DINT occupies 32 bits and is two's-complement signed. The mathematical range is:
DINT range: -2,147,483,648 ... 0 ... +2,147,483,647
The constant 4,294,967,295 is exactly 2^32 - 1 and lies outside the DINT positive range. When the runtime coerces the bit pattern 16#4F7FFFFF into a DINT, bit 31 is interpreted as a sign bit, producing a negative interpretation in the high range or a positive wrap to 1,333,788,671 in the user's example.
Constraint 2: 32-Bit REAL Cannot Represent 4,294,967,295 Exactly
IEEE-754 single precision uses 1 sign bit, 8 exponent bits, and a 23-bit mantissa (24 bits of precision with the implicit leading 1). The largest integer that can be represented exactly is 2^24 = 16,777,216. Values above this boundary are quantized to multiples of 2:
| Hex Pattern | REAL Value | Notes |
|---|---|---|
| 16#4F7FFFFF | 4,294,967,040.0 | Largest REAL < 2^32 (mantissa all-ones) |
| 16#4F800000 | 4,294,967,296.0 | First REAL ≥ 2^32 (mantissa = 0, exponent incremented) |
| 16#4F800001 | 4,294,967,296.0 + 256.0 | Next representable REAL above 2^32 |
The runtime cannot store the literal 4,294,967,295.3 in a 32-bit REAL. It rounds to the nearest representable value, which is 4,294,967,040.0 with the hex bit pattern 16#4F7FFFFF. The TRUNC instruction then performs the bit-level conversion and the user observes 1,333,788,671 at MD.
Data Type Comparison on S7-300/400 vs S7-1200/1500
| Keyword | Bits | Signed? | Range | STEP 7 v5.5 (S7-300/400) | TIA Portal (S7-1200/1500) |
|---|---|---|---|---|---|
| INT | 16 | Yes | -32,768 to +32,767 | Yes | Yes |
| DINT | 32 | Yes | -2,147,483,648 to +2,147,483,647 | Yes | Yes |
| UINT | 16 | No | 0 to 65,535 | No (use WORD + cast) | Yes (V14+) |
| UDINT | 32 | No | 0 to 4,294,967,295 | No native type | Yes (V14+) |
| DWORD | 32 | N/A | Bit pattern 16#00000000 to 16#FFFFFFFF | Yes | Yes |
| REAL | 32 | Yes | ±3.402823e+38 (≈ 7 decimal digits) | Yes (IEEE-754 single) | Yes |
| LREAL | 64 | Yes | ±1.797693e+308 (≈ 15 decimal digits) | No | Yes (S7-1500 only) |
| LWORD | 64 | N/A | Bit pattern | No | Yes (S7-1500 only) |
DWORD, which the editor treats as a bit-string without a numeric interpretation. Conversion math must be implemented explicitly in STL/SCL if the project must remain on S7-300/400 firmware.Bit-Level Walkthrough: Why TRUNC Returns 1,333,788,671
- Compiler loads constant
4,294,967,295.3into the REAL tag (MD100). The value is quantized to the nearest IEEE-754 single:16#4F7FFFFF = 4,294,967,040.0. -
TRUNCinstruction is called with IN = MD100 and OUT = MD104 (DINT). The instruction copies the 32 bits of the source verbatim into the destination register; the runtime does not re-interpret sign. - Online monitor on MD104 displays the value as signed DINT. Bit 31 of
16#4F7FFFFFis0(positive), so the displayed integer is the lower 31 bits:0x4F7FFFFF = 1,333,788,671. - If the value had been rounded up to
16#4F800000, bit 31 would be1, and the displayed DINT would be-2,147,483,648— an apparent negative result that alarms most operators.
Both outcomes are correct bit-copy operations but incorrect integer conversions because DINT is the wrong destination type for any value > 2,147,483,647.
Solution Path Selection
Choose a path based on controller generation and the magnitude of the data being processed.
| If your project runs on | And the value is | Use this fix |
|---|---|---|
| S7-300/400 (STEP 7 v5.5) | ≤ 2,147,483,647 | Validate input range; TRUNC/ROUND work correctly |
| S7-300/400 (STEP 7 v5.5) | ≤ 4,294,967,295 (unsigned 32-bit) | Move to DWORD; perform unsigned compare in STL |
| S7-300/400 (STEP 7 v5.5) | > 16,777,216 (REAL precision loss) | Scale input to engineering units first; or use 64-bit FB library |
| S7-1200/1500 (TIA Portal) | ≤ 4,294,967,295 (unsigned 32-bit) | Declare tag as UDINT; use REAL_TO_UDINT
|
| S7-1500 (TIA Portal) | > 4,294,967,295 (64-bit float) | Use LREAL; REAL_LREAL_TO_UDINT from extended instructions |
Solution 1: Range Validation (S7-300/400, all platforms)
Before calling TRUNC or ROUND, test whether the REAL input exceeds the DINT positive boundary. The pattern is implemented in STL below; the same logic can be built in LAD/FBD with two comparators.
// STL example - validate REAL input range
A "input_real" // REAL tag, e.g. MD100
L 2.147483648e+09 // DINT positive max as REAL
>R // input_real > max?
JC OVER // jump if overflow
// input is safe; proceed
TRUNC // out = DINT
T "output_dint"
JU CONT
oVER: L 0
T "output_dint" // clamp to 0, or set error flag
SET
S "overflow_flag" // raise diagnostic bit
CONT: NOP 0
Add an underflow test against -2.147483648e+09 to fully cover the negative half. This pattern alone resolves 80% of field complaints when the underlying process value legitimately fits in 31 bits.
Solution 2: Use DWORD as a 32-Bit Unsigned Container (S7-300/400)
STEP 7 v5.5 has no UDINT, but a DWORD receives the bit pattern of any REAL with full 32-bit fidelity. After moving the REAL to a DWORD, use unsigned STL operations.
// Copy REAL to DWORD, then compare as unsigned
L "input_real" // MD100 - REAL
T "temp_dword" // MD104 - DWORD (same bit pattern)
// Unsigned test against 16#80000000 boundary
L "temp_dword"
L W#16#8000_0000 // 2^31 = 2,147,483,648
>D // unsigned compare
JC NEG_VAL // bit 31 set = value > 2,147,483,647
// Value is in 0..2,147,483,647 - safe to TRUNC
TRUNC
T "output_dint"
JU DONE
NEG_VAL: // Treat as DWORD-only; no DINT math
L "temp_dword"
T "output_dword" // display as 16#... in HMI
DONE: NOP 0
Hex or use a manual scaling factor (e.g., subtract 2^32 in the HMI script) to recover the unsigned value.Solution 3: Native UDINT and LREAL on S7-1200/1500 (TIA Portal)
The S7-1200 (firmware V4.0+) and S7-1500 add full unsigned-integer and 64-bit-float support. Declare the target tag as UDInt or LReal and the compiler selects the correct conversion instruction.
| Source | Destination | Instruction in TIA Portal | Behavior on overflow |
|---|---|---|---|
| REAL | DINT |
REAL_TO_DINT / TRUNC
|
Returns DINT min; sets ENO = 0 |
| REAL | UDINT |
REAL_TO_UDINT (Extended instructions) |
Returns 0; sets ENO = 0 |
| LREAL | DINT | LREAL_TO_DINT |
Returns DINT min; sets ENO = 0 |
| LREAL | UDINT | LREAL_TO_UDINT |
Returns 0; sets ENO = 0 |
| LREAL | LINT | LREAL_TO_LINT |
Returns LINT min; sets ENO = 0 |
The official TIA Portal V20 reference for DINT conversion options is published at Explicit conversion of DINT (S7-1500). Use the ENO output to drive an overflow diagnostic in the same network — a faulted conversion with ENO = 0 is the cleanest way to surface the problem to WinCC Unified or HMI tags.
Solution 4: 64-Bit REAL Library for S7-300/400
For legacy CPUs that must continue in service but handle values > 2^24 with full precision, install a user-defined 64-bit floating-point FB. The reference implementation by Piotr M. is published on the Siemens support portal and includes source SCL for:
-
FB_DoubleFloat_Add— LREAL addition on two 32-bit halves -
FB_DoubleFloat_Sub— LREAL subtraction -
FB_DoubleFloat_Mul— LREAL multiplication (Karatsuba) -
FB_DoubleFloat_Div— LREAL division with Newton-Raphson reciprocal -
FC_DoubleFloat_Trunc— 64-bit REAL → DINT (with overflow flag)
Each function manipulates two DWORDs (high and low halves) and tracks sign, exponent, and 52-bit mantissa independently. The library is the only viable path on an S7-315-2 PN/DP or S7-417H that cannot be replaced.
Step-by-Step Fix (S7-300/400, SCL Example)
The following SCL block is a drop-in replacement for the failing TRUNC network. It performs range validation, returns a DWORD (bit pattern) for the unsigned half, and raises an overflow flag.
FUNCTION_BLOCK FB_RealToUnsigned32
VAR_INPUT
rValue : REAL; // input real
END_VAR
VAR_OUTPUT
dwOut : DWORD; // unsigned 32-bit result
bError : BOOL; // TRUE on overflow / NaN
END_VAR
VAR
dTest : DINT;
END_VAR
BEGIN
bError := FALSE;
// NaN / Inf guard
IF (rValue <> rValue) OR (rValue = 1.0e+38) OR (rValue = -1.0e+38) THEN
dwOut := 16#00000000;
bError := TRUE;
RETURN;
END_IF;
// In-range & positive path
IF rValue >= 0.0 AND rValue <= 4.294967295e+09 THEN
dwOut := REAL_TO_DWORD(rValue);
ELSE
dwOut := 16#00000000;
bError := TRUE;
END_IF;
END_FUNCTION_BLOCK
Compile the source into the S7 program (SCL compiler V5.3+), call the FB in OB1 or OB35, and route bError to a retentive marker for HMIs.
Verification
-
Online watch table: Force the input REAL tag to
4.294967040e+09and confirm the DWORD output readsDW#16#4F7FFFFF. Display in HEX mode in the watch table to avoid signed interpretation. -
Boundary tests: Force
2,147,483,647.0(max DINT) and4,294,967,295.0(max UDINT). Both must return the correct hex pattern and an unambiguous overflow flag. -
HMI round-trip: Display the DWORD tag in WinCC flexible / TIA HMI configured as Hexadecimal 32-bit. Verify the operator sees
0x4F7FFFFF, not1,333,788,671. -
Cross-check with calculator: Convert
4,294,967,295decimal to hex — result is0xFFFFFFFF. Any block returning a different pattern under any input indicates a misconfigured data type or a sign-extended move. -
Regression test: Capture the original fault scenario (REAL = 4,294,967,295.3) and confirm the FB returns
dwOut = 0x4F7FFFFFandbError = TRUE.
Troubleshooting Matrix
| Observed Result | Likely Cause | Corrective Action |
|---|---|---|
| 1,333,788,671 | TRUNC on REAL ≈ 4.29e9, signed DINT view | Switch destination to DWORD, or scale input |
| -2,147,483,648 | Bit 31 set in source REAL (≥ 2^31) | Use DWORD, validate range upstream |
| 0 with ENO = 0 in TIA | Overflow detected by REAL_TO_UDINT
|
Inspect input source; widen to LREAL if needed |
| HMI shows -1 instead of 4,294,967,295 | DWORD tag displayed as signed DINT | Change HMI tag format to Hex or unsigned Decimal 32 |
| Step7 rejects UDINT declaration | STEP 7 v5.5 does not support UDINT | Use DWORD; convert to unsigned math manually |
| REAL drifts after 16,777,216 | 24-bit mantissa limit of single precision | Scale value to engineering units (÷1000) before conversion |
Engineering Best Practices
-
Document the implicit assumption. If a tag holds a value > 2^24, mark it in the symbol table with the suffix
_L(large) and route it through LREAL or the 64-bit library. - Always wire ENO. On S7-1200/1500, the explicit conversion instructions report overflow through ENO. Ignoring ENO hides every range bug that this article describes.
- Standardize HMI formats. Configure every DWORD tag in WinCC Unified / flexible with the Hexadecimal or Decimal unsigned 32-bit representation. The signed-decimal default is the root of the most common operator complaints.
-
Validate in OB100 / startup. Run range and NaN checks once on first scan and latch a
CFG_ERRbit. A misconfigured REAL constant at load time is otherwise silent until the first conversion fires. - Prefer LREAL on new S7-1500 projects. Even when 32-bit range is sufficient today, a future sensor or counter change can push the value past 2^24. Starting in LREAL is cheaper than retrofitting a fleet.
FAQ
Why does TRUNC return 1,333,788,671 when I pass 4,294,967,295.3?
The 32-bit REAL cannot store 4,294,967,295.3 exactly; it rounds to 4,294,967,040.0 with the bit pattern 16#4F7FFFFF. TRUNC copies those 32 bits into a DINT, and the PLC's signed interpretation yields +1,333,788,671. The value 4,294,967,295 is also outside the DINT range (-2,147,483,648 to +2,147,483,647), so DINT is the wrong destination type.
Does Siemens S7-300/400 support UDINT in STEP 7 v5.5?
No. STEP 7 v5.5 has no native UDINT. Use DWORD as a 32-bit unsigned container and perform any unsigned math in STL with the >D, <D, and unsigned-move primitives. UDINT and UINT are introduced for S7-1200/1500 in TIA Portal V14+.
What is the difference between DINT, UDINT, and DWORD?
DINT is a 32-bit signed integer (-2,147,483,648 to +2,147,483,647). UDINT is a 32-bit unsigned integer (0 to 4,294,967,295). DWORD is a 32-bit bit string with no numeric interpretation. Bits 0-31 of any DINT or UDINT can be re-interpreted as a DWORD without losing information.
Can I represent 4,294,967,295 exactly in a 32-bit REAL?
No. The nearest 32-bit REALs are 4,294,967,040.0 (16#4F7FFFFF) and 4,294,967,296.0 (16#4F800000). To represent values above 2^24 = 16,777,216 without precision loss, use 64-bit LREAL on S7-1500 or the 64-bit FP library on S7-300/400.
How do I detect an overflow in TIA Portal explicit conversion?
Every explicit conversion instruction in TIA Portal (REAL_TO_DINT, REAL_TO_UDINT, LREAL_TO_LINT, etc.) sets ENO = 0 on overflow, NaN, or out-of-range input. Wire ENO to a diagnostic flag and the conversion result to a safe default; see the Siemens TIA Portal V20 reference for DINT conversion for the full instruction list.