Type-conversion functions

The naming pattern

Every conversion is <SRC>_TO_<DST> where source and target are IEC type names:

<integer>_TO_<integer>     INT_TO_DINT, DINT_TO_INT, SINT_TO_LINT, ...
<integer>_TO_<unsigned>    INT_TO_UINT, DINT_TO_UDINT, ...
<unsigned>_TO_<integer>    UINT_TO_INT, UDINT_TO_DINT, ...
<integer>_TO_<bit-string>  INT_TO_BYTE, INT_TO_WORD, INT_TO_DWORD, ...
<bit-string>_TO_<integer>  BYTE_TO_INT, WORD_TO_DINT, ...
<num>_TO_REAL              INT_TO_REAL, DINT_TO_LREAL, ...
TRUNC                      REAL → DINT (truncate toward zero)
TRUNC_INT                  REAL → INT
TRUNC_LINT                 LREAL → LINT
<num>_TO_STRING            INT_TO_STRING, REAL_TO_STRING, ...
STRING_TO_<num>            STRING_TO_INT, STRING_TO_REAL, ...
<bool>_TO_<x>              BOOL_TO_INT, BOOL_TO_BYTE (TRUE → 1)
<x>_TO_BOOL                INT_TO_BOOL (0 → FALSE, anything else → TRUE)
TIME_TO_<x>                TIME_TO_DINT (returns ms), TIME_TO_REAL
<x>_TO_TIME                DINT_TO_TIME (interprets as ms)
DATE_TO_DT                 DATE → DATE_AND_TIME
DT_TO_DATE                 DATE_AND_TIME → DATE
DT_TO_TOD                  DATE_AND_TIME → TIME_OF_DAY
BCD_TO_INT                 BCD-encoded BYTE/WORD → INT/DINT
INT_TO_BCD                 INT/DINT → BCD-encoded BYTE/WORD

Range and precision

Conversions that lose information silently truncate or wrap. Some examples:

ConversionLossy when
DINT_TO_INTsource out of -32 768..32 767 → wraps
INT_TO_BYTEsource out of 0..255 → wraps
REAL_TO_INT (via TRUNC_INT)source out of INT range → wraps; fractional digits dropped
STRING_TO_INTsource not parseable → returns 0 (matiec); silent failure
LREAL_TO_REALsource has more than ~7 significant digits → rounded

For safety-critical conversions, range-check before the call:

IF dwSource >= -32768 AND dwSource <= 32767 THEN
    iTarget := DINT_TO_INT(dwSource);
ELSE
    (* handle out-of-range *)
END_IF;

TRUNC vs. ROUND

IEC 61131-3 only standardises TRUNC (truncate toward zero). There is no ROUND, CEILING, FLOOR in the standard library. If you need rounding, do it by hand:

(* round to nearest INT *)
iRounded := REAL_TO_DINT(rValue + SEL(rValue < 0.0, 0.5, -0.5));

(* CEILING: round up *)
iCeiling := TRUNC_DINT(rValue + 0.999999);

(* FLOOR: round down *)
iFloor := TRUNC_DINT(rValue);
IF rValue < 0.0 AND (REAL_TO_DINT(rValue) <> rValue) THEN
    iFloor := iFloor - 1;
END_IF;

BCD encoding

BCD_TO_INT / INT_TO_BCD convert between binary integer and BCD (binary-coded decimal) packed into a BYTE / WORD. BCD is what older 7-segment-display drivers and some legacy PLC interfaces expose.

(* read packed BCD from a hardware register *)
iValue := BCD_TO_INT(bRawHardware);

(* write to a 7-segment driver *)
bDisplay := INT_TO_BCD(iValue);

IEC reference

IEC 61131-3 third edition (2013), Annex F.2.1 — “Type-conversion functions” Table F.1.

matiec conformance

matiec implements every conversion in the table. Two practical notes:

  • String parsing (STRING_TO_INT, STRING_TO_REAL) returns 0 on parse failure rather than raising; always validate the source string yourself if the input is user-controlled.
  • No automatic widening: an INT argument to a function expecting DINT requires an explicit INT_TO_DINT call. See the llm_signals block for the canonical fix.

ForgeIEC notes

The editor’s auto-completion suggests the matching <SRC>_TO_<DST> call when it detects a type-mismatch in an expression. The MCP forge.help_for tool consults this page’s llm_signals to translate “incompatible types” errors into actionable conversion-call insertions.