Standard Functions¶
Function lookup for shipped IEC function families, signatures, overload families, coverage status, per-function IEC references, and documented truST extensions such as assertion helpers.
Related: Standard Function Blocks, Data Types
IEC 61131-3 Edition 3.0 (2013) - Section 6.6.2.5
This specification defines standard-function signatures for trust-hir and the bounded runtime results explicitly written below.
1. Overview¶
Standard functions are predefined functions available in all IEC 61131-3 implementations.
Function Index¶
| Name / Group | Category | Signature Shape | IEC ref | Status |
|---|---|---|---|---|
*_TO_*, TO_*, TRUNC_*, *_BCD_TO_*, *_TO_BCD_* |
Conversion | fixed or overloaded | Tables 22-27 | Implemented with documented extensions |
ABS, SQRT, LN, LOG, EXP, SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2 |
Numerical | fixed arity | Table 28 | Implemented |
ADD, SUB, MUL, DIV, MOD, EXPT, MOVE |
Arithmetic | fixed/extensible | Table 29 | Implemented |
SHL, SHR, ROL, ROR |
Bit shift / rotate | fixed arity | Table 30 | Implemented |
AND, OR, XOR, NOT |
Bitwise boolean | fixed/extensible | Table 31 | Implemented |
SEL, MAX, MIN, LIMIT, MUX |
Selection | fixed/extensible | Table 32 | Implemented |
GT, GE, EQ, LE, LT, NE |
Comparison | fixed/extensible | Table 33 | Implemented |
LEN, LEFT, RIGHT, MID, CONCAT, INSERT, DELETE, REPLACE, FIND |
String | fixed/extensible | Table 34 | Implemented |
ADD_*, SUB_*, MUL_*, DIV_*, CONCAT_*, SPLIT_*, DAY_OF_WEEK |
Date / time | fixed or overloaded | Tables 35-36 | Implemented |
IS_VALID, IS_VALID_BCD |
Validate | fixed arity | Table 39 | Implemented |
REF |
Reference | fixed arity | Table 12 | Implemented |
LOWER_BOUND, UPPER_BOUND |
Array bound | fixed arity | IEC extension set | Implemented |
ASSERT_* |
Test assertions | fixed arity | non-IEC | truST test-workflow extension |
Function Characteristics¶
- No internal state (stateless)
- For deterministic stateless operations, identical inputs under the same fixed execution/numeric contract produce the same specified result. Host-clock extensions have their own environmental input contract.
- Standard function results follow their input semantics under the declared numerical accuracy contract; this is not a promise of identical floating-point bits for unspecified-precision operations, even within one build. IEC 61131-3:2013 §6.6.2.5.8 requires implementation-specific accuracy dependencies to be stated. The planned shared runtime contract and its exact/tolerance-qualified claims are defined in specification 34 §12.
- Can be overloaded for different types
- Some have extensible inputs (e.g., ADD can take 2+ arguments)
2. Type Conversion Functions (Tables 22-27)¶
Conversion functions use the SRC_TO_DST form (Table 22). The overloaded TO_DST form exists but is deprecated.
Truncation forms:
- TRUNC (deprecated overloaded)
- TRUNC_<DST> (overloaded, e.g., TRUNC_INT)
- <SRC>_TRUNC_<DST> (typed, deprecated, e.g., REAL_TRUNC_INT)
When STRING/WSTRING is an input or output, the string shall conform to the external representation of the corresponding data type.
2.1 Numeric Conversions¶
Integer to Integer¶
| Function | From | To | Notes |
|---|---|---|---|
*_TO_SINT |
ANY_INT | SINT | Truncation may occur |
*_TO_INT |
ANY_INT | INT | Truncation may occur |
*_TO_DINT |
ANY_INT | DINT | |
*_TO_LINT |
ANY_INT | LINT | |
*_TO_USINT |
ANY_INT | USINT | Unsigned conversion |
*_TO_UINT |
ANY_INT | UINT | |
*_TO_UDINT |
ANY_INT | UDINT | |
*_TO_ULINT |
ANY_INT | ULINT |
Real to Integer¶
| Function | Notes |
|---|---|
REAL_TO_INT |
IEC 60559 round-to-nearest, ties to even |
LREAL_TO_INT |
IEC 60559 round-to-nearest, ties to even |
TRUNC |
Deprecated overloaded truncation toward zero |
TRUNC_* |
Overloaded truncation toward zero (e.g., TRUNC_INT) |
*_TRUNC_* |
Typed truncation toward zero (deprecated) |
Integer to Real¶
| Function | Notes |
|---|---|
INT_TO_REAL |
Exact for small integers |
*_TO_LREAL |
More precision |
Real to Real¶
| Function | Notes |
|---|---|
REAL_TO_LREAL |
Widening |
LREAL_TO_REAL |
Narrowing and precision loss; a finite source that overflows basic-single range returns RuntimeError::Overflow |
For an explicit numeric or binary-transfer conversion whose target is REAL
or LREAL, truST returns RuntimeError::Overflow if the computed result would
be NaN or either infinity. This includes LREAL_TO_REAL narrowing and the IEC
Table 25 binary transfers DWORD_TO_REAL and LWORD_TO_LREAL. Finite binary
transfers retain their exact bit-defined value. Text parsing rejects the
non-finite spellings specified in section 2.7, but that rejection does not
promise an exact runtime-error variant. The rejection policy is
implementer-specific under IEC 61131-3 section 6.6.2.5.2 and Table 23; see
docs/IEC_DECISIONS.md#2026-07-22---non-finite-real-result-and-explicit-conversion-policy.
For conversion into SINT, INT, DINT, or LINT, a representable integer
value is preserved exactly. A value outside the destination's IEC Table 10
range returns RuntimeError::Overflow; truST does not wrap, saturate,
truncate, or substitute an integer. This is the reviewed implementer-specific
conversion fault policy specified by
docs/specs/10-runtime-semantics.md#6-8-signed-integer-result-materialization;
it is not an IEC deviation.
2.2 Bit Data Type Conversions (Table 24)¶
Binary transfer between BYTE/WORD/DWORD/LWORD. If the target is wider, the rightmost bits are preserved and the remaining bits are set to zero. If the target is narrower, only the rightmost bits are kept.
BYTE_TO_WORD,BYTE_TO_DWORD,BYTE_TO_LWORDWORD_TO_BYTE,WORD_TO_DWORD,WORD_TO_LWORDDWORD_TO_BYTE,DWORD_TO_WORD,DWORD_TO_LWORDLWORD_TO_BYTE,LWORD_TO_WORD,LWORD_TO_DWORDBYTE_TO_BOOL,WORD_TO_BOOL,DWORD_TO_BOOL,LWORD_TO_BOOL: binary transfer of the rightmost bit into the target (Table 24 rows 4, 8, 12 and 16), soBYTE_TO_BOOL(2#1101_0100) = FALSEandWORD_TO_BOOL(16#0003) = TRUE. The overloadedTO_BOOLof a bit string does the same.
2.3 Bit/Numeric Conversions (Table 25)¶
Binary transfer between bit strings and numeric types as listed in Table 25:
- Bit to numeric: BYTE/WORD/DWORD/LWORD to SINT/INT/DINT/LINT/USINT/UINT/UDINT/ULINT,
DWORD_TO_REAL and LWORD_TO_LREAL
- Numeric to bit: SINT/INT/DINT/LINT/USINT/UINT/UDINT/ULINT to BYTE/WORD/DWORD/LWORD,
REAL_TO_DWORD and LREAL_TO_LWORD
- BOOL to SINT/INT/DINT/LINT/USINT/UINT/UDINT/ULINT results in 0 or 1
- Table 25 has no other pair of a bit string and a real (no DWORD_TO_LREAL, no WORD_TO_REAL),
so a bit string's numeric value reaches a real through an integer:
UDINT_TO_LREAL(DWORD_TO_UDINT(d)).
- A bit string takes no arithmetic operator: the arithmetic functions take ANY_NUM (§6.6.2.5.8,
Table 29), so its value is converted to an integer first, as DWORD_TO_UDINT(TIME_TO_DWORD(t))
is before it is divided.
REAL/LREAL to a bit string of another width and BOOL to REAL/LREAL are
not in Table 25; truST provides them as the extensions of section 2.9.
2.4 Date and Time Conversions (Table 26)¶
| Function | Description |
|---|---|
LTIME_TO_TIME |
LTIME to TIME |
TIME_TO_LTIME |
TIME to LTIME |
LDT_TO_DT |
LDT to DT |
LDT_TO_DATE |
Extract DATE from LDT |
LDT_TO_LTOD |
Extract LTOD from LDT |
LDT_TO_TOD |
Extract TOD from LDT (precision loss possible) |
DT_TO_LDT |
DT to LDT |
DT_TO_DATE |
Extract DATE from DT |
DT_TO_LTOD |
Extract LTOD from DT |
DT_TO_TOD |
Extract TOD from DT |
LTOD_TO_TOD |
LTOD to TOD |
TOD_TO_LTOD |
TOD to LTOD |
Implementer extension note:
- truST additionally provides TIME_TO_DWORD and DWORD_TO_TIME as
documented non-IEC conversion helpers.
- These extensions use milliseconds:
TIME_TO_DWORD(T#123ms) = DWORD#123,
DWORD_TO_TIME(DWORD#123) = T#123ms.
- Under a CODESYS vendor_profile (specification 14), truST additionally
provides duration-to-integer and time-of-day-to-dword conversions as
documented non-IEC helpers (IEC_DEVIATIONS.md 2026-10-28); the IEC
dialect rejects them:
TIME_TO_<int> and LTIME_TO_<int> yield the whole millisecond
(respectively nanosecond) count, kept to the destination's low-order bits
as the other CODESYS integer conversions are (section 2.10):
TIME_TO_DINT(T#1500ms) = 1500, TIME_TO_SINT(T#300ms) = 44;
TOD_TO_DWORD and LTOD_TO_DWORD yield the milliseconds of the day:
TOD_TO_DWORD(TOD#01:00:00) = 3600000.
A negative duration rejects with an overflow fault, matching the
TIME_TO_DWORD contract.
- Round 2 (IEC_DEVIATIONS.md 2026-11-06), under the CODESYS profile only,
the remaining inverses as whole families; the IEC dialect rejects them:
<ANY_INT>_TO_TIME reads the integer as a millisecond count through its
low-order 32 bits (measured: DINT_TO_TIME(DINT#-1) = T#49d17h2m47s295ms,
INT_TO_TIME(INT#-1) = T#49d17h2m47s295ms);
REAL_TO_TIME/LREAL_TO_TIME read the value as milliseconds truncated
toward zero — truncation, not the DEV-024 rounding applied to
REAL/LREAL to integer — and a negative value saturates to T#0ms
(measured: REAL_TO_TIME(2.5) = T#2ms, REAL_TO_TIME(-1.0) = T#0ms,
section 2.10); DWORD_TO_TOD yields the ms-of-day magnitude modulo 24 h
(measured: DWORD_TO_TOD(DWORD#86400000) = TOD#0:0:0,
DWORD_TO_TOD(DWORD#16#FFFFFFFF) = TOD#17:02:47.295).
2.5 Character Type Conversions (Table 27)¶
| Function | Description |
|---|---|
WSTRING_TO_STRING |
Convert WSTRING to STRING |
WSTRING_TO_WCHAR |
First character of WSTRING |
STRING_TO_WSTRING |
Convert STRING to WSTRING |
STRING_TO_CHAR |
First character of STRING |
WCHAR_TO_WSTRING |
Single-character WSTRING |
WCHAR_TO_CHAR |
Convert WCHAR to CHAR |
CHAR_TO_STRING |
Single-character STRING |
CHAR_TO_WCHAR |
Convert CHAR to WCHAR |
Implementer extension note:
- truST also accepts direct character-to-bitstring conversions such as
CHAR_TO_BYTE and WCHAR_TO_WORD as documented vendor extensions.
- Other conversions involving STRING/WSTRING (for example, numeric to string)
remain implementer-specific. When provided, they shall follow the external
literal representation rules in 6.3.3.
2.6 BCD Conversions (Table 22)¶
| Function | Description |
|---|---|
*_BCD_TO_** |
Typed BCD conversion from BYTE/WORD/DWORD/LWORD to USINT/UINT/UDINT/ULINT |
BCD_TO_** |
Overloaded BCD conversion (bit string to unsigned integer) |
**_TO_BCD_* |
Typed BCD conversion from USINT/UINT/UDINT/ULINT to BYTE/WORD/DWORD/LWORD |
TO_BCD_** |
Overloaded BCD conversion (unsigned integer to bit string) |
// Example
BCDValue := 16#0042;
UIntValue := BCD_TO_UINT(BCDValue); // UIntValue = 42
2.7 Runtime Text-Conversion Representatives¶
IEC 61131-3 Ed.3 section 6.6.2.5.2 requires conversions involving
STRING/WSTRING to use the external representation of the source or
destination type, while conversion-error handling remains
implementer-specific. The bounded truST runtime contract contains these exact
representatives:
| Call | Result |
|---|---|
DINT_TO_STRING(DINT#42) |
STRING#'42' |
REAL_TO_STRING(REAL#1.25) |
STRING#'1.25' |
DWORD_TO_STRING(DWORD#42) |
STRING#'42' |
STRING_TO_DINT(STRING#'42') |
DINT#42 |
Vendor-extension representatives (IEC_DEVIATIONS.md 2026-10-28), under a
CODESYS vendor_profile only: TO_STRING and the typed *_TO_STRING
spellings accept a BOOL ('TRUE'/'FALSE'), a TIME/LTIME (the
vendor's duration text), and an enumerated value (its variant name); the
IEC dialect rejects them. The duration text is T# and each unit that is
not zero from days down to milliseconds, T#0ms for no time; an LTIME
has the prefix LTIME# and units down to nanoseconds, LTIME#0ns for none
(measured on a live e!COCKPIT runtime on a WAGO PFC200, 2026-11-06:
TO_STRING(T#1500ms) = 'T#1s500ms', TO_STRING(T#2000ms) = 'T#2s',
TO_STRING(T#1h2m3s4ms) = 'T#1h2m3s4ms', TO_STRING(T#2d) = 'T#2d',
TO_STRING(T#49d17h2m47s295ms) = 'T#49d17h2m47s295ms',
TO_STRING(T#0ms) = 'T#0ms', TIME_TO_STRING(T#1500ms) = 'T#1s500ms',
TO_STRING(LTIME#1500ms) = 'LTIME#1s500ms',
TO_STRING(LTIME#1h2m3s4ms5us6ns) = 'LTIME#1h2m3s4ms5us6ns',
TO_STRING(LTIME#0ns) = 'LTIME#0ns'). A TIME's part below a millisecond
is left out, as the vendor's TIME counts milliseconds, and a negative
duration, which the vendor's unsigned types cannot hold, has its sign after
the # as an IEC literal does ('T#-1s500ms'). In every dialect, TO_STRING over a STRING[n]
value is an identity copy. Time-of-day and date string forms are
deliberately not provided.
Round 2 representatives (IEC_DEVIATIONS.md 2026-11-06), under a CODESYS
vendor_profile only: STRING_TO_<int>, STRING_TO_<bit string> and the
overloaded TO_<int>/TO_<bit string> spellings accept a STRING, all
with one parse rule — leading spaces, an optional sign, a decimal or
2#/8#/16# literal, trailing characters ignored; text with no number
yields 0 and an out-of-range value keeps the low-order bits of the
destination (measured on a live e!COCKPIT runtime:
TO_BYTE('123abc') = 123, TO_BYTE('300') = 44, TO_BYTE('abc') = 0,
TO_BYTE('') = 0). The IEC dialect keeps the strict representative parse
(STRING_TO_DINT('123abc') faults) and rejects string-to-bit-string
conversions.
These representatives do not define canonical output spelling for every
numeric or bit-string value. STRING_TO_REAL('NaN') and
STRING_TO_LREAL('inf') reject without producing a runtime REAL/LREAL
value. This specification intentionally does not freeze the runtime-error
variant used for those two text-rejection cases.
The compact text implementation shall preserve the existing conversion contract
on every platform. Decimal parsing uses correctly rounded binary64 conversion
before the existing destination conversion, including subnormal values and signed
zero; lossy parsing is forbidden. Formatting preserves the prior shortest
round-tripping binary64 digits expanded into decimal notation, with the existing
.0 suffix for integral real conversion output. REAL continues to widen to
binary64 before formatting. Normalization, malformed-input errors and non-finite
rejection remain unchanged. Compact lookup tables are an implementation choice,
not a per-device capability restriction (IEC §6.6.2.5.2, Table 23).
2.8 Runtime Conversion Dispatch and Boundary Contract¶
The following requirements bind runtime execution of IEC 61131-3 Tables 22-27 and the truST conversion extensions documented above:
- Conversion names are ASCII case-insensitive.
SRC_TO_DSTandSRC_TRUNC_DSTfirst normalize the argument to the named source type: an integer or real source accepts a value of its family (ANY_INTorANY_REAL) when that value is representable in the source type (for a real source: finite after IEC 60559 rounding to the source width), and raisesRuntimeError::Overflowotherwise, soSINT_TO_BYTE(INT#256)faults before theBYTEconversion; a BCD source requires exactly its declared type and raisesRuntimeError::TypeMismatchfor any other.TO_DST,TRUNC_DST, andTRUNCinfer the source from the runtime value. Every conversion requires exactly one argument. - Ordinary real-to-integer conversions round to nearest with ties to even.
TRUNC,TRUNC_DST, andSRC_TRUNC_DSTtruncate toward zero. Non-finite inputs and results outside the complete destination range raiseRuntimeError::Overflow; this includes theULINTupper boundary. - Integer narrowing and signed-to-unsigned conversion are checked numeric
conversions. Bit-string transfers are different: widening zero-extends and
narrowing preserves the rightmost destination-width bits. Bit-string to a
signed integer sign-extends from the destination width, and bit-string to
BOOLkeeps the rightmost bit. REAL_TO_DWORD,DWORD_TO_REAL,LREAL_TO_LWORD, andLWORD_TO_LREALare exact binary transfers for finite values. A transferred NaN or infinity is rejected asRuntimeError::Overflow.- Text-to-integer conversion trims surrounding whitespace, accepts single
_separators between two digits (the IEC 61131-3 §6.3.2 rule for numeric literals), and acceptsbase#digitsfor bases 2 through 36 with an optional sign ondigits. An empty value, invalid digit, leading, trailing or repeated_, or base outside 2 through 36 raisesRuntimeError::TypeMismatch; malformed input never panics. - Text-to-real conversion accepts finite Rust/IEC-compatible decimal and
exponent text after whitespace and
_normalization, with the same single-separator rule. NaN, infinity, and a finite decimal whose destination result becomes non-finite raiseRuntimeError::Overflow. - Numeric and bit-string text output is decimal. Integral finite real output
retains a
.0suffix. Character conversion requires exactly one Unicode scalar for text input and checks the targetCHAR/WCHARcode range. - A text result longer than the implementer maximum of 65,535 bytes
(
STRING) or UTF-16 code units (WSTRING) raisesRuntimeError::StringCapacityExceeded(IEC 61131-3 Ed.3 §6.6.2.5.11), for example aWSTRING_TO_STRINGof 30,000 characters that each need three bytes of UTF-8. - BCD encoding accepts unsigned integers only and fails with
RuntimeError::Overflowwhen the decimal digits do not fit the destination bit string. BCD decoding rejects every nibble above nine withRuntimeError::TypeMismatchand checks the unsigned destination range. - Short
DTextraction uses Euclidean day division at the one-millisecond default profile, so instants before the epoch produce the precedingDATEand a non-negativeTOD. Long-to-short conversion floors sub-millisecond instants consistently.TIME_TO_DWORDcounts whole milliseconds and rejects negative or greater-than-u32::MAXvalues.
2.9 Vendor Extension Conversions¶
truST also accepts these conversions, which IEC 61131-3 Tables 22-27 do not list but CODESYS and TwinCAT projects use. They follow the truST conversion rules above (no wrapping or saturation):
| Function | Result |
|---|---|
REAL_TO_BYTE, REAL_TO_WORD, REAL_TO_LWORD, LREAL_TO_BYTE, LREAL_TO_WORD, LREAL_TO_DWORD |
The number converted like REAL_TO_<unsigned> of the target width (USINT, UINT, UDINT, ULINT): round to nearest, ties to even; a value outside the target range or a non-finite value returns RuntimeError::Overflow. REAL_TO_DWORD and LREAL_TO_LWORD remain the Table 25 binary transfers. |
<ANY_INT>_TO_BOOL, <ANY_REAL>_TO_BOOL, and TO_BOOL of such a value |
TRUE when the value is not zero. A non-finite REAL/LREAL returns RuntimeError::Overflow. |
BOOL_TO_REAL, BOOL_TO_LREAL |
1.0 for TRUE, 0.0 for FALSE. |
Examples: REAL_TO_BYTE(200.4) = BYTE#200, REAL_TO_BYTE(256.0) returns
RuntimeError::Overflow, INT_TO_BOOL(-3) = TRUE, INT_TO_BOOL(0) = FALSE.
BYTE/WORD/DWORD/LWORD_TO_BOOL are the IEC Table 24 conversions of section 2.2,
not extensions.
2.10 CODESYS Conversion Profile¶
A project selects the conversion rules with the vendor_profile of its manifest, the
same setting the language server reads ([project] vendor_profile in trust-lsp.toml,
see docs/public/reference/config/vendor-profiles.md):
vendor_profile |
Conversion rules |
|---|---|
codesys, beckhoff, twincat |
CODESYS rules (this section); TwinCAT is built on CODESYS |
absent, iec, siemens, mitsubishi, gxworks3 |
IEC rules and truST policies (sections 2.1 to 2.9) |
| any other value | refused: the project is not built, run or tested, and the language server reports C006 |
Every tool that executes a project's code applies the project's rules: trust-runtime
build, check and run (from a project folder or a runtime.toml folder), trust-dev
test, debug sessions (whose project folder holds the manifest, io.toml or
runtime.toml), and trust-harness started with --project DIR. A trust-harness
load request may name "vendor_profile" itself, which takes precedence over
--project; without either it uses the IEC rules. In the Rust API the rules are
CompileSession::with_conversion_profile and TestHarness::from_session. Editor
diagnostics do not depend on the conversion rules: the type checker accepts the same
conversions under both. A running runtime keeps the rules it started with; an online
change does not switch them.
Under the CODESYS rules the conversion functions follow the CODESYS Development System instead of the IEC rules and truST policies above:
| Conversion | iec (default) |
codesys |
|---|---|---|
REAL/LREAL to an integer or bit string |
round to nearest, ties to even | round half away from zero (.1-.4 down, .5-.9 up): REAL_TO_INT(2.5) = 3, REAL_TO_INT(-1.5) = -2 |
a value outside the target range (integer narrowing, signed/unsigned change, REAL to integer) |
RuntimeError::Overflow |
the low-order bits of the target width, two's complement: DINT_TO_SINT(128) = -128, INT_TO_UINT(-1) = 65535, REAL_TO_BYTE(300.0) = 44 |
REAL_TO_DWORD, LREAL_TO_LWORD |
Table 25 binary transfer | the number: REAL_TO_DWORD(3.7) = 4 |
DWORD_TO_REAL, LWORD_TO_LREAL |
Table 25 binary transfer | the number: DWORD_TO_REAL(5) = 5.0 |
BYTE/WORD/DWORD/LWORD_TO_BOOL (and TO_BOOL of a bit string) |
Table 24: the rightmost bit | TRUE when the value is not zero: BYTE_TO_BOOL(16#80) = TRUE |
<ANY_INT>_TO_TIME (an integer read as a millisecond count) |
Table 26 lists none | the low-order 32 bits of the count: DINT_TO_TIME(DINT#-1) = T#49d17h2m47s295ms (measured) |
REAL_TO_TIME, LREAL_TO_TIME (the value read as milliseconds) |
Table 26 lists none | truncated toward zero, negative saturates to T#0ms: REAL_TO_TIME(2.5) = T#2ms, REAL_TO_TIME(-1.0) = T#0ms (measured) |
DWORD_TO_TOD |
Table 26 lists none | the ms-of-day magnitude modulo 24 h: DWORD_TO_TOD(DWORD#86400000) = TOD#0:0:0, DWORD_TO_TOD(DWORD#16#FFFFFFFF) = TOD#17:02:47.295 (measured) |
STRING_TO_<int>, STRING_TO_<bit string>, TO_<int>(STRING), TO_<bit string>(STRING) |
STRING_TO_<int> keeps the strict representative parse of section 2.7; string to a bit string rejects |
one CODESYS parse rule: leading spaces, an optional sign, a decimal or 2#/8#/16# literal, trailing characters ignored, no number yields 0, out of range keeps the low-order bits: TO_BYTE('123abc') = 123, TO_BYTE('300') = 44, TO_BYTE('abc') = 0 (measured) |
The round-2 values above (2026-11-06) are measured on a live e!COCKPIT
runtime on a WAGO PFC200, not derived from the vendor documentation
(IEC_DEVIATIONS.md 2026-11-06). The rounding row does not extend to
REAL_TO_TIME/LREAL_TO_TIME: the vendor truncates toward zero there
(REAL_TO_TIME(2.5) = T#2ms, measured — despite the DEV-024 rounding of
REAL/LREAL to an integer), and a negative REAL/LREAL value
saturates to T#0ms (REAL_TO_TIME(-1.0), measured). A negative integer
count is different: the vendor TIME is a 32-bit unsigned count, so the
integer reads through its low-order 32 bits
(DINT_TO_TIME(DINT#-1) = T#49d17h2m47s295ms, measured). The vendor
string parse applies to every string-to-integer and string-to-bit-string
conversion under the profile, typed and overloaded alike, and consumes _
inside the number as a digit separator (TO_BYTE('1_0') = 10, measured):
TO_BYTE('123abc') and STRING_TO_DINT('123abc') are both 123 — one
parse rule per profile.
A non-finite REAL/LREAL source still returns RuntimeError::Overflow, and conversions
whose CODESYS result is "undefined, target system-dependent" keep the low-order bits. The
profile changes conversion functions only; arithmetic overflow, implicit conversions and
the set of available conversions are unchanged.
The out-of-range results are implementer-specific under IEC 61131-3 Ed.3 6.6.2.5.3 and
Table 23. Three CODESYS rules conflict with normative IEC requirements and are recorded in
docs/IEC_DEVIATIONS.md: the rounding of ties (IEC_DEVIATIONS 2026-10-28), the numeric REAL/LREAL and
DWORD/LWORD conversions (IEC_DEVIATIONS 2026-10-28) and the bit string to BOOL test (IEC_DEVIATIONS 2026-10-28).
References: CODESYS Development System,
REAL_TO / LREAL_TO
and integer conversions
("If the number to be converted exceeds the range limit, then the first bytes of the number are
ignored"); for TO_BOOL, the CODESYS-based
ABB Automation Builder documentation.
3. Numerical Functions (Table 28)¶
Basic Arithmetic¶
| Function | Description | Signature |
|---|---|---|
ABS |
Absolute value | ABS(x: ANY_NUM) : ANY_NUM |
SQRT |
Square root | SQRT(x: ANY_REAL) : ANY_REAL |
LN |
Natural logarithm | LN(x: ANY_REAL) : ANY_REAL |
LOG |
Base 10 logarithm | LOG(x: ANY_REAL) : ANY_REAL |
EXP |
Exponential (e^x) | EXP(x: ANY_REAL) : ANY_REAL |
Trigonometric Functions (Table 28)¶
| Function | Description | Domain | Range |
|---|---|---|---|
SIN |
Sine | Radians | -1.0 to 1.0 |
COS |
Cosine | Radians | -1.0 to 1.0 |
TAN |
Tangent | Radians | Real |
ASIN |
Arc sine | -1.0 to 1.0 | -π/2 to π/2 |
ACOS |
Arc cosine | -1.0 to 1.0 | 0 to π |
ATAN |
Arc tangent | Real | -π/2 to π/2 |
ATAN2 |
Arc tangent (y/x) | Real, Real | -π to π |
// Examples
Y := SIN(X); // X in radians
Angle := ATAN2(DY, DX); // Four-quadrant arctangent
Arithmetic Functions (Table 29)¶
| Function | Description | Signature |
|---|---|---|
ADD |
Addition | ADD(IN1, IN2, ...: ANY_NUM) : ANY_NUM |
MUL |
Multiplication | MUL(IN1, IN2, ...: ANY_NUM) : ANY_NUM |
SUB |
Subtraction | SUB(IN1, IN2: ANY_NUM) : ANY_NUM |
DIV |
Division | DIV(IN1, IN2: ANY_NUM) : ANY_NUM |
MOD |
Modulo | MOD(IN1, IN2: ANY_INT) : ANY_INT |
EXPT |
Exponentiation | EXPT(IN1: ANY_REAL, IN2: ANY_NUM) : ANY_REAL |
MOVE |
Assignment | MOVE(IN: ANY) : ANY |
Note: ADD and MUL are extensible (can take more than 2 inputs).
The EXPT standard-function signature remains the IEC ANY_REAL-base
contract shown above. The reviewed INT#2 ** INT#3 host-evaluator extension
is separate and is recorded in
IEC_DEVIATIONS.md;
it does not widen the standard-function signature.
For finite REAL or LREAL operands, EXP and EXPT return a value only
when the result remains finite at the operand's IEC basic width (single for
REAL, double for LREAL). A result outside that finite range raises
RuntimeError::Overflow before assignment storage and leaves the target
unchanged. The runtime does not clamp or store infinity or NaN. EXPT(0.0,
0.0) is 1.0; a zero base with a negative exponent, and a negative base with
a non-integer exponent, have no finite result and raise Overflow. Results
keep IEC 60559 signed zeros and subnormal values, and an underflow to a
subnormal value or zero is not a fault. See
docs/specs/10-runtime-semantics.md and
docs/IEC_DECISIONS.md#2026-07-22---non-finite-real-result-and-explicit-conversion-policy.
3.1 Runtime Numerical Conformance Contract¶
The following requirements bind the runtime implementation of IEC 61131-3 Tables 28 and 29:
ABSpreserves the operand's elementary numeric type. Unsigned values are returned unchanged. The most-negative value of each signed integer type raisesRuntimeError::Overflow; it is never wrapped.ABS(-0.0)is+0.0.MODreturnsIN1 - (IN1 / IN2) * IN2with integer division truncating toward zero (IEC 61131-3 Table 29), so a nonzero result has the sign ofIN1.MODby zero raisesRuntimeError::ModuloByZero; IEC Table 29 defines that case as0, and the deviation is recorded indocs/IEC_DEVIATIONS.md.- The named real functions accept
REALorLREAL. AREALinput produces aREALresult and anLREALinput produces anLREALresult. A domain error or non-finite result raisesRuntimeError::Overflow. ATAN2(Y, X)accepts matching real widths and the mixedREAL/LREALpairs. Either mixed pair producesLREAL.- Extensible
ADDandMULrequire at least two inputs and evaluate from left to right.SUB,DIV,MOD, andEXPTrequire exactly two inputs, whileMOVErequires exactly one. - Date/time participation makes
ADDa two-input operation. Duration multiplication accepts the duration on either side of the scale factor; duration division requires the duration on the left. These operations preserveTIMEversusLTIME. - Arithmetic errors, including division by zero and an unrepresentable intermediate or result, are returned as runtime errors. No later variadic input is evaluated after an earlier fold step fails.
MOVEreturns an equal clone of its input value without numeric widening or other conversion.
4. Bit Shift Functions (Table 30)¶
| Function | Description | Signature |
|---|---|---|
SHL |
Shift left | SHL(IN: ANY_BIT, N: ANY_INT) : ANY_BIT |
SHR |
Shift right | SHR(IN: ANY_BIT, N: ANY_INT) : ANY_BIT |
ROL |
Rotate left | ROL(IN: ANY_BIT, N: ANY_INT) : ANY_BIT |
ROR |
Rotate right | ROR(IN: ANY_BIT, N: ANY_INT) : ANY_BIT |
// Examples
X := 2#1100_0000;
Y := SHL(X, 2); // Y = 2#0000_0000 (bits shifted out)
Z := ROL(X, 2); // Z = 2#0000_0011 (bits rotated)
IN must be a bit-string value (ANY_BIT); an integer, real or other IN
raises RuntimeError::TypeMismatch.
5. Bitwise Boolean Functions (Table 31)¶
| Function | Description | Signature |
|---|---|---|
AND |
Bitwise AND | AND(IN1, IN2, ...: ANY_BIT) : ANY_BIT |
OR |
Bitwise OR | OR(IN1, IN2, ...: ANY_BIT) : ANY_BIT |
XOR |
Bitwise XOR | XOR(IN1, IN2, ...: ANY_BIT) : ANY_BIT |
NOT |
Bitwise NOT | NOT(IN: ANY_BIT) : ANY_BIT |
Note: AND, OR, XOR are extensible.
At runtime, shift and rotate functions preserve the input bit-string type and
width. Bitwise Boolean functions return the common participating bit-string
width; a narrower input is zero-extended to that width. The reviewed mixed
representative is
OR(BYTE#16#0F, WORD#16#00F0) = WORD#16#00FF.
Shift counts are non-negative integers. For SHL and SHR, a count greater
than or equal to the operand width produces zero at that same width. For ROL
and ROR, the count is reduced modulo the operand width; zero and exact
multiples of the width are identity operations for every bit-string width,
including LWORD. A negative count raises RuntimeError::TypeMismatch.
Extensible AND, OR, and XOR require at least two inputs. Their result
width is the widest participating bit-string width, narrower values are
zero-extended, and the final result is masked to that width. NOT requires one
input, flips only the bits inside its declared width, and preserves that width.
// Examples
Mask := 16#FF00;
Data := 16#1234;
Result := AND(Data, Mask); // Result = 16#1200
Result := OR(Data, 16#00FF); // Result = 16#12FF
6. Selection Functions (Table 32)¶
| Function | Description | Signature |
|---|---|---|
SEL |
Binary selection | SEL(G: BOOL, IN0, IN1: ANY) : ANY |
MAX |
Maximum | MAX(IN1, IN2, ...: ANY_ELEMENTARY) : ANY_ELEMENTARY |
MIN |
Minimum | MIN(IN1, IN2, ...: ANY_ELEMENTARY) : ANY_ELEMENTARY |
LIMIT |
Bounded value | LIMIT(MN, IN, MX: ANY_ELEMENTARY) : ANY_ELEMENTARY |
MUX |
Multiplexer | MUX(K: ANY_INT, IN0, IN1, ...: ANY) : ANY |
// SEL: Returns IN0 if G=FALSE, IN1 if G=TRUE
Result := SEL(Condition, ValueIfFalse, ValueIfTrue);
// MAX/MIN
MaxValue := MAX(A, B, C, D);
MinValue := MIN(A, B, C, D);
// LIMIT: Clamps IN between MN and MX
Output := LIMIT(0, Input, 100); // 0 <= Output <= 100
// MUX: Returns IN[K]
Selected := MUX(Index, Value0, Value1, Value2, Value3);
The runtime returns the selected input with its runtime value identity intact,
including an enumerated value selected by SEL. Named invocation binds the
documented formal names; the reviewed executed-ST representative
SEL(G := TRUE, IN0 := INT#4, IN1 := INT#7) returns INT#7.
6.1 Runtime Selection Conformance Contract¶
SELrequires aBOOLselector and exactly two data inputs.FALSEselectsIN0;TRUEselectsIN1.MINandMAXrequire at least two inputs and compare every input after resolving one common elementary runtime type.LIMITfirst resolvesMN,IN, andMXto one common type, then returnsMIN(MAX(IN, MN), MX)(IEC 61131-3 Table 32):MNwhenIN < MN,MXwhenIN > MX, andINotherwise. With inverted limits (MN > MX) the result isMX.MUXinterpretsKas a zero-based input number. A negative or out-of-range selector raisesRuntimeError::IndexOutOfBounds. All candidate inputs must belong to one valid common type even when the selected candidate itself would otherwise be usable.- Common numeric widening is applied before returning a selection result.
Incompatible signed/unsigned, narrow/wide string, time-family, or enum-family
inputs raise
RuntimeError::TypeMismatch.
7. Comparison Functions (Table 33)¶
| Function | Description | Signature |
|---|---|---|
GT |
Greater than | GT(IN1, IN2, ...: ANY_ELEMENTARY) : BOOL |
GE |
Greater or equal | GE(IN1, IN2, ...: ANY_ELEMENTARY) : BOOL |
EQ |
Equal | EQ(IN1, IN2, ...: ANY_ELEMENTARY) : BOOL |
LE |
Less or equal | LE(IN1, IN2, ...: ANY_ELEMENTARY) : BOOL |
LT |
Less than | LT(IN1, IN2, ...: ANY_ELEMENTARY) : BOOL |
NE |
Not equal | NE(IN1, IN2: ANY_ELEMENTARY) : BOOL |
Note: For GT, GE, EQ, LE, LT with multiple inputs, checks if sequence is monotonic. NE is not extensible.
For values of the same enumerated type, runtime EQ and NE compare the
enumerated value identity. The reviewed representatives are EQ(RED, RED) =
TRUE and NE(RED, GREEN) = TRUE.
The runtime comparison contract for IEC 61131-3 Table 33 is pairwise and
adjacent: GT(A, B, C) means (A > B) AND (B > C), with equivalent chaining
for GE, EQ, LE, and LT. Those functions require at least two inputs;
NE requires exactly two. Before comparison, all inputs resolve to one common
elementary runtime type. Numeric widths may widen where the conversion is
lossless under the normal numeric policy, while bit strings widen by
zero-extension. Strings, time values, and enumerations compare only within
their compatible family; incompatible families raise
RuntimeError::TypeMismatch.
// Examples
InOrder := GT(A, B, C); // TRUE if A > B > C
AllEqual := EQ(X, Y, Z); // TRUE if X = Y = Z
Different := NE(A, B); // TRUE if A <> B
8. String Functions (Table 34)¶
String declaration syntax (STRING[n], WSTRING[n]) and character indexing are
owned by 02-data-types.md. This section owns the callable string functions.
| Function | Description | Signature |
|---|---|---|
LEN |
Length | LEN(IN: ANY_STRING) : INT |
LEFT |
Left substring | LEFT(IN: ANY_STRING, L: ANY_INT) : ANY_STRING |
RIGHT |
Right substring | RIGHT(IN: ANY_STRING, L: ANY_INT) : ANY_STRING |
MID |
Middle substring | MID(IN: ANY_STRING, L, P: ANY_INT) : ANY_STRING |
CONCAT |
Concatenate | CONCAT(IN1, IN2, ...: ANY_STRING) : ANY_STRING |
INSERT |
Insert string | INSERT(IN1, IN2: ANY_STRING, P: ANY_INT) : ANY_STRING |
DELETE |
Delete substring | DELETE(IN: ANY_STRING, L, P: ANY_INT) : ANY_STRING |
REPLACE |
Replace substring | REPLACE(IN1, IN2: ANY_STRING, L, P: ANY_INT) : ANY_STRING |
FIND |
Find position | FIND(IN1, IN2: ANY_STRING) : INT |
// Examples
Str := 'Hello World';
Length := LEN(Str); // 11
Left5 := LEFT(Str, 5); // 'Hello'
Right5 := RIGHT(Str, 5); // 'World'
Mid := MID(Str, 5, 7); // 'World' (5 chars starting at pos 7)
Full := CONCAT('Hello', ' ', 'World'); // 'Hello World'
Inserted := INSERT('AC', 'B', 1); // 'ABC'
Deleted := DELETE('ABCD', 2, 2); // 'AD' (delete 2 chars at pos 2)
Replaced := REPLACE('ABCD', 'XX', 2, 2); // 'AXXD'
Pos := FIND('ABCABC', 'BC'); // 2 (first occurrence)
Position Notes:
- Position 1 is the first character
- FIND returns 0 if not found
- Runtime positions and lengths count Unicode scalar values (characters),
not UTF-8 bytes (DEV-017). Thus LEN('ÄBC') = 3, LEFT('ÄBC', 1) = 'Ä',
and FIND('ÄBC', 'B') = 2. The same element-count rule applies to the
reviewed WSTRING length.
- A declared capacity counts storage units instead: STRING[n] holds n
bytes of UTF-8 and WSTRING[n] n UTF-16 code units (02-data-types.md
§6). 'ÄBC' is three characters in four bytes, so it fits STRING[4] but
not STRING[3].
8.1 Runtime String Boundary Contract¶
- Every result preserves the input family:
STRINGoperations returnSTRING, andWSTRINGoperations returnWSTRING. Binary and variadic operations reject mixed narrow/wide inputs. LEFTandRIGHTreturn the empty string for a non-positive length and the whole string when the requested length exceeds the element count.MID,DELETE, andREPLACEuse one-based positions. Positions below one are clamped to the first element. A start beyond the final element returns the empty string forMIDand leaves the input unchanged forDELETEandREPLACE. A length that runs past the final element is clamped to it.INSERTtreatsPas the number of existing elements before the insertion: zero inserts before the first element,P = LEN(IN1)appends, and larger values also append. For example,INSERT('AC', 'B', 1) = 'ABC'.DELETEwith a non-positive length leaves the input unchanged.REPLACEwith a non-positive length inserts the replacement at its clamped start without deleting an element.FINDreturns the one-based position of the first match and zero when no match exists. An emptyIN2matches at position 1. A found position that cannot be represented asINTraisesRuntimeError::Overflow.- A result longer than the implementer maximum of 65,535 bytes (
STRING) or UTF-16 code units (WSTRING) is an error (IEC 61131-3 Ed.3 §6.6.2.5.11, Table 34):CONCAT,INSERT,REPLACE,LEFT,RIGHT,MIDandDELETEraiseRuntimeError::StringCapacityExceeded(runtime_string_capacity_exceeded) and return no value. The conversions toSTRINGandWSTRING(§2) apply the same maximum; aWSTRINGof 65,535 code units can need up to 196,605 bytes asSTRING. - The internal
__TRUST_LIMIT_STRING(IN, L)assignment helper keeps the longest prefix of whole characters withinLbytes of UTF-8 (STRING) orLUTF-16 code units (WSTRING), returns a value that fits unchanged, preserves the string family, and rejects a negative or otherwise non-u32capacity withRuntimeError::Overflow.
9. Date and Time Functions (Tables 35-36)¶
Time Arithmetic¶
| Function | Description |
|---|---|
ADD |
Overloaded time/date addition (see Table 35): the time of day or date and time first, the duration second, as the + operator (specification 05); the duration first is InvalidArgumentType |
ADD_TIME |
TIME + TIME → TIME |
ADD_LTIME |
LTIME + LTIME → LTIME |
ADD_TOD_TIME |
TOD + TIME → TOD |
ADD_LTOD_LTIME |
LTOD + LTIME → LTOD |
ADD_DT_TIME |
DT + TIME → DT |
ADD_LDT_LTIME |
LDT + LTIME → LDT |
SUB |
Overloaded time/date subtraction (see Table 35) |
SUB_TIME |
TIME - TIME → TIME |
SUB_LTIME |
LTIME - LTIME → LTIME |
SUB_DATE_DATE |
DATE - DATE → TIME |
SUB_LDATE_LDATE |
LDATE - LDATE → LTIME |
SUB_TOD_TIME |
TOD - TIME → TOD |
SUB_LTOD_LTIME |
LTOD - LTIME → LTOD |
SUB_TOD_TOD |
TOD - TOD → TIME |
SUB_LTOD_LTOD |
LTOD - LTOD → LTIME |
SUB_DT_TIME |
DT - TIME → DT |
SUB_LDT_LTIME |
LDT - LTIME → LDT |
SUB_DT_DT |
DT - DT → TIME |
SUB_LDT_LDT |
LDT - LDT → LTIME |
MUL_TIME |
TIME * ANY_NUM → TIME |
MUL_LTIME |
LTIME * ANY_NUM → LTIME |
DIV_TIME |
TIME / ANY_NUM → TIME |
DIV_LTIME |
LTIME / ANY_NUM → LTIME |
Notes:
- Overloaded ADD/SUB apply only within the TIME/DT/DATE/TOD set or the LTIME/LDT/LDATE/LTOD set.
- Result range overflow is an error; output ranges are Implementer specific.
IEC 61131-3 Ed.3 section 6.6.2.5.12 and Table 35 define the accepted operand and result families and require an error when the result exceeds the implementer-specific output range. Within that boundary, truST applies this product contract:
TIMEandLTIMEarithmetic preserves the operand width and uses checked signed nanoseconds; a result outsidei64isRuntimeError::Overflow.- Short
DATE,TOD, andDTvalues use the activeDateTimeProfile.resolution. A duration is converted to whole profile ticks by truncating toward zero. Short date/time differences convert their signed tick difference back to a checkedTIMEduration. - Long
LDATE,LTOD, andLDTvalues operate directly in signed nanoseconds and produceLTIMEfor same-family subtraction. - Adding a duration to
TOD,LTOD,DT, orLDTdoes not wrap at a day boundary. It returns the checked signed result in the same value family. MUL_TIME,MUL_LTIME,DIV_TIME, andDIV_LTIMEare the named forms of the*and/duration operators (IEC 61131-3 Table 35) and compute exactly as the operators do. They accept signed or unsigned integer andREAL/LREALfactors. An integer factor scales the nanosecond count exactly, with integer division truncating toward zero; a real factor scales it in binary64 and the real result truncates toward zero. Zero division returnsRuntimeError::DivisionByZero; a non-numeric factor returnsRuntimeError::TypeMismatch; a non-finite factor or an out-of-range result returnsRuntimeError::Overflow.- Ordering compares stored ticks or nanoseconds only when both operands have
the same runtime date/time family. Cross-family ordering returns
RuntimeError::TypeMismatch.
Runtime Clock Sources (TIME, CURRENT_DT)¶
TIME() is a zero-argument truST product function that returns the supplied
logical elapsed duration exactly. Supplying any actual argument is a
compile-time wrong-argument-count error and is also rejected at the runtime
dispatch boundary.
CURRENT_DT() is a zero-argument truST product function. IEC 61131-3 Ed.3
§6.4.2, Table 10, footnote b permits an implementer-defined DT range and
precision; IEC does not define this host-clock function.
The function has this complete product contract:
- it samples
std::time::SystemTimeonce per call and interprets that sample as a UTC Unix timestamp; on a device without an operating system it reads the device's clock, which counts fromDT#1970-01-01-00:00:00at power-up until a time source sets it (specification 34, TARGET-06); - it returns a timezone-naive
DTwhose epoch isDT#1970-01-01-00:00:00and whose fixed resolution is one millisecond; - it truncates a positive sub-millisecond remainder toward the preceding millisecond;
- it accepts Unix timestamps from tick
0throughi64::MAXmilliseconds, inclusive; - a host value before the Unix epoch or above the representable millisecond
tick range returns
RuntimeError::Overflowand produces noDTvalue; - local timezone, daylight-saving, and leap-second metadata are neither read nor encoded;
- the injected runtime/manual clock, scheduler scaling, simulation time, and replay time do not replace or offset the host sample; and
- samples are not clamped to be monotonic. A host-clock rollback may therefore
make a later call return an earlier
DT.
Deterministic runtime replay does not claim identical results for a program
that calls CURRENT_DT() unless the surrounding environment controls the host
clock. Programs that require replay-controlled elapsed time use TIME()
instead. CURRENT_DT rejects every argument during HIR validation and at the
runtime dispatch boundary.
Declared Clock Sources (reads_host_clock)¶
A POU can declare that it samples the host wall clock with the pragma
attribute reads_host_clock. The flag carries no value:
{attribute 'reads_host_clock'}
FUNCTION FuGetDateAndTime : DT
FuGetDateAndTime := FuGetDateAndTime;
END_FUNCTION
A declared POU is itself a clock source, on the same terms as CURRENT_DT:
deterministic replay does not control it, and a program that calls it needs a
controlled host clock for identical results. The declaration makes no claim
about how the samples are taken; it declares that the POU reads the host
clock, which is what a vendor library documents and what a body-less stub
cannot say in code.
The declaration is read from the pragma trivia that precedes the POU's
declaration keyword. A pragma inside the POU's body does not declare
anything, and a POU whose body also reads CURRENT_DT is one source, not
two. A declaration-only stub that carries the flag warns nowhere in its own
file: the declaration speaks for the body the stub replaces, and the warnings
appear at the call sites, like those of a linked library body (specification
14 §7.6). Specification 28 §5 defines the warning and its message.
Date/Time Component Functions¶
| Function | Description |
|---|---|
CONCAT_DATE_TOD |
Combine DATE and TOD into DT |
CONCAT_DATE_LTOD |
Combine DATE and LTOD into LDT |
CONCAT_DATE |
YEAR, MONTH, DAY → DATE |
CONCAT_TOD |
HOUR, MINUTE, SECOND, MILLISECOND → TOD |
CONCAT_LTOD |
HOUR, MINUTE, SECOND, MILLISECOND → LTOD |
CONCAT_DT |
YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND → DT |
CONCAT_LDT |
YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND → LDT |
SPLIT_DATE |
DATE → YEAR, MONTH, DAY |
SPLIT_TOD |
TOD → HOUR, MINUTE, SECOND, MILLISECOND |
SPLIT_LTOD |
LTOD → HOUR, MINUTE, SECOND, MILLISECOND |
SPLIT_DT |
DT → YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND |
SPLIT_LDT |
LDT → YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND |
DAY_OF_WEEK |
DATE → 0=Sunday..6=Saturday |
The CONCAT_* functions accept only valid components, as a date or time
literal does: month 1 to 12, a day that exists in that month, hour 0 to 23,
minute 0 to 59, second 0 to 59 and millisecond 0 to 999. Any component
outside its range, including an invalid calendar date, raises
RuntimeError::Overflow; a component never carries into the next field.
Notes:
- SPLIT_* output types are ANY_INT; the Implementer specifies concrete types.
- Additional inputs/outputs (for example, microsecond/nanosecond) are Implementer specific.
// Examples
NewTime := ADD_TIME(T#1h, T#30m); // T#1h30m
EndTime := ADD_TOD_TIME(TOD#08:00:00, T#2h); // TOD#10:00:00
Duration := SUB_DT_DT(EndDateTime, StartDateTime);
DoubleTime := MUL_TIME(BaseTime, 2);
HalfTime := DIV_TIME(BaseTime, 2);
10. Validate Functions (Table 39)¶
IS_VALID requires exactly one REAL or LREAL and returns whether it is
finite. IS_VALID_BCD requires exactly one BYTE, WORD, DWORD, or
LWORD and checks every nibble in the declared width; it returns FALSE for
an invalid nibble rather than raising a BCD decoding error. The type checker
rejects an unsupported argument type at compile time; a dynamic call that
reaches the runtime with one raises RuntimeError::TypeMismatch. Validation functions do not
modify their input.
| Function | Description | Signature |
|---|---|---|
IS_VALID |
Returns FALSE for invalid real values such as NaN or infinity |
IS_VALID(IN: REAL/LREAL) : BOOL |
IS_VALID_BCD |
Returns FALSE if any BCD nibble is greater than 9 |
IS_VALID_BCD(IN: BYTE/WORD/DWORD/LWORD) : BOOL |
IEC 61131-3 Ed.3 Figure 5 places BOOL under the broader ANY_BIT hierarchy,
but the Table 39 validation-function narrative defines IS_VALID_BCD for
BYTE, WORD, DWORD, and LWORD. truST follows that stricter Table 39
domain and rejects BOOL for IS_VALID_BCD.
VAR
R : REAL;
W : WORD := WORD#16#1234;
Ok : BOOL;
END_VAR
Ok := IS_VALID(R);
Ok := IS_VALID_BCD(W);
11. Reference Functions¶
| Function | Description | Signature |
|---|---|---|
REF |
Get reference | REF(IN: ANY) : REF_TO ANY |
VAR
MyInt: INT := 42;
pInt: REF_TO INT;
END_VAR
pInt := REF(MyInt);
12. Array Bound Functions¶
| Function | Description | Signature |
|---|---|---|
LOWER_BOUND |
Lower array bound | LOWER_BOUND(arr: ARRAY, dim: INT) : DINT |
UPPER_BOUND |
Upper array bound | UPPER_BOUND(arr: ARRAY, dim: INT) : DINT |
VAR
Data: ARRAY[5..15] OF INT;
Lo, Hi: DINT;
END_VAR
Lo := LOWER_BOUND(Data, 1); // Lo = 5
Hi := UPPER_BOUND(Data, 1); // Hi = 15
13. Error Conditions¶
Runtime Errors¶
| Function | Error Condition |
|---|---|
SQRT |
Negative input |
LN, LOG |
Non-positive input |
DIV, MOD |
Division by zero |
ASIN, ACOS |
Input outside [-1, 1] |
STRING_TO_* |
Invalid string format |
| Array bound | Invalid dimension |
Overflow¶
Numeric functions may overflow. Behavior is Implementer specific: - Saturation to max/min value - Wrap-around - Error flag/exception
Implementation Notes for trust-hir¶
Function Resolution¶
- Match function name (case-insensitive)
- Check argument count (consider extensible functions)
- Resolve overloaded variants by argument types
- Apply implicit conversions if needed
- Determine return type
Type Inference for Overloaded Functions¶
// ADD is overloaded for all numeric types
A: INT;
B: INT;
C := ADD(A, B); // C is INT
X: REAL;
Y: REAL;
Z := ADD(X, Y); // Z is REAL
Extensible Functions¶
These functions accept variable number of inputs:
- ADD, MUL (arithmetic)
- AND, OR, XOR (bitwise)
- MAX, MIN (selection)
- GT, GE, EQ, LE, LT (comparison)
- CONCAT (string)
- MUX (selection)
Standard Library¶
The trust-hir should include definitions for all standard functions with: - Name - Parameter types (considering overloading) - Return type - Extensibility flag - Built-in implementation or intrinsic marker
At runtime, standard-function registration and lookup normalize names to
ASCII uppercase. Fixed parameter metadata preserves the documented formal
order. Extensible metadata preserves fixed leading parameters, the uppercase
variadic prefix, its first numeric suffix, and its minimum count. Conversion
functions are parsed on demand after registered-function lookup; an unrecognized
name raises RuntimeError::UndefinedFunction.
Runtime Shared-Helper Contract¶
The common runtime path used by numerical, bitwise, selection, comparison, assertion, and timer functions obeys these rules:
- Exact and minimum arity failures return
RuntimeError::InvalidArgumentCountwith the required count and observed count. - Common-type resolution widens compatible numeric and bit-string values,
preserves narrow versus wide string families, and requires an identical
date/time or enumeration family. Incompatible signed/unsigned or unrelated
elementary families return
RuntimeError::TypeMismatch. - Common-value coercion is checked. It cannot silently wrap an out-of-range
numeric value.
CHARmay joinSTRINGandWCHARmay joinWSTRINGas a one-element string. - Common comparison applies the requested relation after coercion. Floating
comparison follows finite IEEE ordering; NaN is not equal to itself and
satisfies
NE. - Bit extraction and reconstruction preserve the declared widths 1, 8, 16,
32, and 64. Masks include exactly the requested low bits, with every width
of 64 or greater producing the full
u64mask. - Duration scaling follows the
*and/duration operators exactly (section 9): integer factors scale exactly and a real result truncates toward zero. It rejects division by zero and returns overflow rather than wrapping an unrepresentable result.
The runtime clock dispatcher receives normalized uppercase names. TIME
returns the supplied logical elapsed duration exactly. CURRENT_DT follows
the host-clock contract above. Any other name returns
RuntimeError::UndefinedFunction carrying the original unknown name without
normalization or substitution.
Non-IEC Extensions (MP-014)¶
The following functions are non-IEC additions for the user-facing ST test framework:
| Function | Signature | Behavior |
|---|---|---|
ASSERT_TRUE |
ASSERT_TRUE(IN: BOOL) : VOID |
Fails test if IN is not TRUE |
ASSERT_FALSE |
ASSERT_FALSE(IN: BOOL) : VOID |
Fails test if IN is not FALSE |
ASSERT_EQUAL |
ASSERT_EQUAL(EXPECTED: ANY_ELEMENTARY, ACTUAL: ANY_ELEMENTARY) : VOID |
Fails test when values are not equal |
ASSERT_NOT_EQUAL |
ASSERT_NOT_EQUAL(EXPECTED: ANY_ELEMENTARY, ACTUAL: ANY_ELEMENTARY) : VOID |
Fails test when values are equal |
ASSERT_GREATER |
ASSERT_GREATER(VALUE: ANY_ELEMENTARY, BOUND: ANY_ELEMENTARY) : VOID |
Fails test unless VALUE > BOUND |
ASSERT_LESS |
ASSERT_LESS(VALUE: ANY_ELEMENTARY, BOUND: ANY_ELEMENTARY) : VOID |
Fails test unless VALUE < BOUND |
ASSERT_GREATER_OR_EQUAL |
ASSERT_GREATER_OR_EQUAL(VALUE: ANY_ELEMENTARY, BOUND: ANY_ELEMENTARY) : VOID |
Fails test unless VALUE >= BOUND |
ASSERT_LESS_OR_EQUAL |
ASSERT_LESS_OR_EQUAL(VALUE: ANY_ELEMENTARY, BOUND: ANY_ELEMENTARY) : VOID |
Fails test unless VALUE <= BOUND |
ASSERT_NEAR |
ASSERT_NEAR(EXPECTED: ANY_NUM, ACTUAL: ANY_NUM, DELTA: ANY_NUM) : VOID |
Fails test when ABS(EXPECTED-ACTUAL) > DELTA |
Compatibility notes:
- These assertions are truST test-workflow extensions and are not part of IEC
61131-3 Tables 22-36 or Table 39.
- They are intended for TEST_PROGRAM / TEST_FUNCTION_BLOCK execution paths.
- On success, every ASSERT_* runtime call returns Value::Null, the runtime
representation of the IEC-facing VOID result.
- The reviewed mixed numeric comparisons are lossless INT/DINT equality
and ordering, plus finite REAL/LREAL comparison for ASSERT_NEAR. Other
mixed elementary-type pairs are not authorized by these representatives.
- A condition failure returns RuntimeError::AssertionFailed; it does not
return a normal value.
- Failure messages use user-facing value text rather than internal Value
debug forms. For the reviewed value families, integers use decimal text,
integral REAL values retain .0, Boolean values use TRUE/FALSE, and
CHAR values use single quotes.
- The stable message forms for the reviewed relational assertions are:
- ASSERT_EQUAL failed: expected {expected}, actual {actual}
- ASSERT_NOT_EQUAL failed: values should differ, left {left}, right {right}
- ASSERT_GREATER failed: value {value} is not greater than bound {bound}
- ASSERT_LESS failed: value {value} is not less than bound {bound}
- ASSERT_GREATER_OR_EQUAL failed: value {value} is not >= bound {bound}
- ASSERT_LESS_OR_EQUAL failed: value {value} is not <= bound {bound}
- An ASSERT_NEAR failure message identifies ASSERT_NEAR and includes its
delta context. No exact full-string format is promised for that message.
Assertion calls enforce their documented arity and reject unsupported operand
families with RuntimeError::TypeMismatch. ASSERT_TRUE and ASSERT_FALSE
accept only BOOL. ASSERT_NEAR accepts finite numeric values, succeeds when
ABS(EXPECTED - ACTUAL) <= DELTA, rejects a negative DELTA as
RuntimeError::AssertionFailed, and rejects non-finite inputs as
RuntimeError::Overflow. The comparison runs in binary64 and also succeeds
when the difference exceeds DELTA by no more than four binary64 epsilons
times the largest of |EXPECTED|, |ACTUAL|, DELTA and 1.0, which
absorbs the rounding of decimal operands such as ASSERT_NEAR(1.0, 1.1,
0.1). A successful assertion always returns Value::Null;
failed assertions never return a normal value.