Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Conversions

<FROM>_TO_<TO>(x) and TO_<TO>(x) convert a value; TRUNC and its named forms cut a real to an integer. Ed.3 6.6.2.5 gives three kinds, and iecf keeps them apart:

  • between numbers — integers and reals — the value is kept if the target holds it;
  • between bit strings, or a bit string and a number, the bits are kept: a binary transfer (Ed.3 Table 25);
  • to and from STRING, the value is written or read as text (Text conversions).

Implicit conversions (Ed.3 Figure 11) only widen: an INT goes where a DINT or a REAL is wanted without a word, and keeps its value.

Implicit conversions: they widen

Ed.3 6.6.1.6 applies Figure 11’s implicit conversions in an assignment, to a function’s or block’s input, to a block’s output taken with =>, and to the operands of an operator or overloaded function, so that both are one type: the narrower one is widened to the other’s. Never to an in-out (rule 6): a VAR_IN_OUT takes a variable of its own type.

An operation is done in the type of its operands, and its result is widened only where it is used (Ed.3 6.6.1.6, Example 3): INT * INT is an INT, and overflows as one, even into a DINT. CODESYS and TwinCAT compute in their registers’ width instead, at least 32 bits, so the same product there is 60000; iecf follows the standard, and flags it. Make one operand wide, INT_TO_DINT(i) * j, for the product in DINT.

An unsigned type widens to the signed type twice its width, keeping its value: UINT#40000 is 40000 as a DINT, not -25536. Two integers neither of which widens to the other are both widened to the narrowest signed type that holds them both, as Siemens SCL does: SINT and USINT to INT, INT and UINT to DINT, DINT and UDINT to LINT. ULINT and LINT have none, and are refused (E0014): convert one.

ExpressionTypeResultFlagNote
DINT#-3 + UINT#40000DINT39997—the UINT widened
UINT#40000 > DINT#-3BOOLTRUE—
SINT#-1LINT-1—the sign kept
USINT#255INT255—
BYTE#16#FF OR WORD#16#0100WORD16#01FF—a bit string: no sign
INT#3 / REAL#2.0REAL1.5—Ed.3 Example 6
MAX(SINT#-1, DINT#-3)DINT-1—
INT#30000 * DINT#2DINT60000—
INT#30000 * INT#2DINT-5536flag OVERFLOWmultiplied as INT, then widened
INT#30000 + UINT#40000DINT70000—both widened to DINT
SINT#-1 + USINT#255INT254—both to INT
DINT#-1 + UDINT#4000000000LINT3999999999—both to LINT
INT#-1 < UINT#40000BOOLTRUE—compared as DINTs
MAX(INT#-1, UINT#40000)DINT40000—

Integer to integer

A value the target holds is kept. One it does not hold keeps its low bits — what a two’s complement machine gives — and is flagged CONVERSION: Siemens’s 2011 manual keeps “the least-significant bytes of IN”, and TwinCAT’s INT_TO_SINT(4223) is 127.

ExpressionTypeResultFlagNote
INT_TO_DINT(INT#-5)DINT-5—
INT_TO_SINT(INT#100)SINT100—
INT_TO_SINT(INT#200)SINT-56flag CONVERSION16#C8
INT_TO_SINT(INT#4223)SINT127flag CONVERSION16#107F: its low byte
DINT_TO_UINT(DINT#-1)UINT65535flag CONVERSION
UINT_TO_INT(UINT#40000)INT-25536flag CONVERSION
ULINT_TO_LINT(ULINT#18446744073709551615)LINT-1flag CONVERSION

Real to integer: rounded half to even

REAL_TO_INT and the others round to the nearest integer, a half to the even one (Ed.3 6.6.2.5.3: “rounding … to the nearest integer”; IEEE 754’s default). TRUNC_INT, REAL_TRUNC_INT and the old TRUNC (which gives DINT and warns, W0003) drop the fraction, toward zero.

A real the target cannot hold — out of its range, an infinity, a NaN — has no low bits to keep, and iecf invents no value: the conversion is flagged CONVERSION and its statement is left undone. An assignment keeps its target’s old value; an IF or CASE runs no branch; a FOR does not run; a WHILE or REPEAT ends; a function block’s input keeps its value, and the block is called. Only the first flag of a scan is kept, so a NaN made by SQRT shows as NAN, not as the conversion after it.

Undone, the statement faults on nothing it computes after: where the conversion’s place in it would divide by zero or index outside an array (10 / REAL_TO_INT(x), a[REAL_TO_INT(x)] := 1), the statement is flagged and left undone, and the task carries on. A fault inside a FUNCTION the statement calls still stops it, as does a fault in a statement whose conversions all succeeded. An assignment’s target may be indexed by a conversion, or by a FUNCTION with effects, computed before the value (Ed.3 7.3.2); the harness case targetidx runs these.

ExpressionTypeResultFlagNote
REAL_TO_INT(REAL#2.6)INT3—
REAL_TO_INT(REAL#2.5)INT2—half to even
REAL_TO_INT(REAL#3.5)INT4—half to even
REAL_TO_INT(REAL#-2.5)INT-2—
REAL_TO_UINT(REAL#-0.4)UINT0—rounds to 0, which UINT holds
REAL_TO_INT(REAL#40000.0)INTunchangedflag CONVERSIONstatement left undone
REAL_TO_UINT(REAL#-1.0)UINTunchangedflag CONVERSION
INT#10 / REAL_TO_INT(REAL#40000.0)INTunchangedflag CONVERSIONno division by zero
LREAL_TO_DINT(SQRT(LREAL#-1.0))DINTunchangedflag NANSQRT’s flag came first
TRUNC_INT(REAL#-2.7)INT-2—toward zero
REAL_TRUNC_DINT(REAL#2.7)DINT2—

Integer to real

The nearest real, half to even. Past 2²⁴ for a REAL, 2⁵³ for an LREAL, not every integer is a real; the nearest is no error and is not flagged.

ExpressionTypeResultFlagNote
DINT_TO_REAL(DINT#16777216)REAL16777216.0—
DINT_TO_REAL(DINT#16777217)REAL16777216.0—the nearest REAL, half to even
INT_TO_LREAL(INT#-3)LREAL-3.0—

Bit strings: a binary transfer

To or from a bit string, the bits move as they are (Ed.3 Table 25): to a narrower type its rightmost (low) bits, to a wider one zeros on the left. A signed integer’s bits are its two’s complement. Nothing is flagged: a bit string has no range to leave.

From BOOL, 1 or 0. To BOOL only with an extension. Ed.3 contradicts itself here: Figure 11, which the checker follows, defines no conversion from BYTE, WORD, DWORD or LWORD to BOOL, while Table 25 lists BYTE_TO_BOOL and the others as “Binary transfer of the rightmost bit into the target” — so BYTE_TO_BOOL(2) would be FALSE. CODESYS and TwinCAT accept them and give TRUE for any value other than 0. iecf refuses them (E0013) unless --extension bit-string-to-bool is on, and then gives what CODESYS and TwinCAT give (decided, florin, extensions). Portable either way: b.0 for the rightmost bit, b <> 0 for any.

ExpressionTypeResultFlagNote
BYTE_TO_BOOL(BYTE#16#02)BOOLTRUE—not the rightmost bit: any value but 0
BYTE_TO_BOOL(BYTE#0)BOOLFALSE—
WORD_TO_BOOL(WORD#16#8000)BOOLTRUE—
DWORD_TO_BOOL(DWORD#1)BOOLTRUE—
LWORD_TO_BOOL(LWORD#16#8000000000000000)BOOLTRUE—
TO_BOOL(BYTE#16#10)BOOLTRUE—the overloaded form
ExpressionTypeResultFlagNote
INT_TO_WORD(INT#-1)WORD16#FFFF—
WORD_TO_INT(WORD#16#8000)INT-32768—
DWORD_TO_BYTE(DWORD#16#12345678)BYTE16#78—the rightmost byte
BYTE_TO_DWORD(BYTE#16#FF)DWORD16#000000FF—zeros on the left
BOOL_TO_WORD(TRUE)WORD16#0001—
BYTE#2 <> 0BOOLTRUE—any bit set

Between a 32 or 64-bit string and REAL or LREAL the bits are the IEEE 754 encoding:

ExpressionTypeResultFlagNote
DWORD_TO_REAL(DWORD#16#3F800000)REAL1.0—
REAL_TO_DWORD(REAL#-2.0)DWORD16#C0000000—
LREAL_TO_LWORD(LREAL#1.0)LWORD16#3FF0000000000000—