FEEL numeric functions: rounding, decimal, modulo and number arithmetic
FEEL guide · 15 built-in functions · DMN 1.6
FEEL has a single number type, a decimal with 34 significant digits. There is no separate integer or float, and no binary floating point, so 0.1 + 0.2 is exactly 0.3. Most numeric questions in DMN modelling come down to two things: which of the seven rounding functions to use, and what happens when an operation is invalid. This guide answers both.
Each result was computed by the FEEL engine behind the evaluator at the top. Click Try it to run any example yourself.
All FEEL numeric functions at a glance
| Function | Signature | Returns |
|---|---|---|
| decimal | decimal(n, scale) | Rounds n to the given scale using banker's rounding (round half to even) |
| floor | floor(n, scale?) | Rounds towards negative infinity at the given scale |
| ceiling | ceiling(n, scale?) | Rounds towards positive infinity at the given scale |
| round up | round up(n, scale?) | Rounds away from zero at the given scale |
| round down | round down(n, scale?) | Rounds towards zero at the given scale |
| round half up | round half up(n, scale?) | Rounds to the nearest value; ties round away from zero (the rounding most people learn in school) |
| round half down | round half down(n, scale?) | Rounds to the nearest value; ties round towards zero |
| abs | abs(n) | Returns the absolute value of a number or duration |
| modulo | modulo(dividend, divisor) | Returns the remainder of the division |
| sqrt | sqrt(number) | Returns the square root; null for negative input |
| log | log(number) | Returns the natural logarithm |
| exp | exp(number) | Returns e raised to the given power |
| odd | odd(number) | Returns true if the number is an odd integer |
| even | even(number) | Returns true if the number is an even integer |
| number | number(from, grouping separator?, decimal separator?) | Parses a string into a number, optionally describing which grouping and decimal separators the string uses |
Numbers, equality and precision
Numbers compare by value, precision aside, so 1 = 1.0 and 1.10 = 1.1 are both true. Division is exact up to 34 significant digits. Comparing a number with a string is not false but null, because the two types cannot be compared.
1 = 1.0
1 = number("1")
Negative numbers and **: -2 ** 2 is 4
In FEEL, arithmetic negation binds more tightly than exponentiation, which is the opposite of the convention in mathematics. DMN 1.6 says so explicitly: -4 ** 2 is (-4) ** 2, which is 16, not -16. The spec recommends always writing the parentheses.
Rounding: which function to use
FEEL has seven rounding functions, and they differ only in what they do with halves and negative numbers. decimal uses banker's rounding (half to even), the IEEE 754 default, which is not what most business users expect. For the rounding taught in school, use round half up. All of them take an optional scale, the number of decimal digits to keep. decimal requires it.
| value | decimal | round half up | round half down | round up | round down | floor | ceiling |
|---|---|---|---|---|---|---|---|
| 2.5 | 2 | 3 | 2 | 3 | 2 | 2 | 3 |
| 3.5 | 4 | 4 | 3 | 4 | 3 | 3 | 4 |
| -2.5 | -2 | -3 | -2 | -3 | -2 | -3 | -2 |
| 2.51 | 3 | 3 | 3 | 3 | 2 | 2 | 3 |
| -2.49 | -2 | -2 | -2 | -3 | -2 | -3 | -2 |
floor(1.567, 2)
In short: round up and round down go away from and towards zero, while floor and ceiling go towards negative and positive infinity. They only disagree on negative numbers.
Division by zero and invalid input
FEEL has no exceptions. An operation that has no valid result, such as dividing by zero, the square root of a negative number or the logarithm of zero, evaluates to null. A string where a number is expected also gives null, with no implicit conversion. In the B-FEEL dialect, numeric built-ins return 0 wherever FEEL returns null (DMN 1.6, section 11.3).
1 / 0
modulo(10, 0)
sqrt(-1)
log(0)
floor(number("1.5"))
Function reference
Each function with its examples and edge cases. The heading links to the function's own page with its parameter table.
decimal
decimal(n, scale)
Rounds n to the given scale using banker's rounding (round half to even).
decimal rounds half to even, also called banker's rounding: 2.5 becomes 2 and 3.5 becomes 4. That avoids a systematic upward bias when many values are summed, but it surprises people checking an invoice by hand. Use round half up for school rounding.
decimal(1/3, 2)
decimal(1.5, 0)
decimal(2.5, 0)
decimal(2.345, 2)
decimal(2.355, 2)
decimal(-2.5, 0)
floor
floor(n, scale?)
Rounds towards negative infinity at the given scale.
floor(1.5)
floor(-1.56, 1)
floor(null)
ceiling
ceiling(n, scale?)
Rounds towards positive infinity at the given scale.
ceiling(1.5)
ceiling(-1.56, 1)
ceiling(-1.5)
round up
round up(n, scale?)
Rounds away from zero at the given scale.
round up(5.5)
round up(-5.5)
round up(1.121, 2)
round down
round down(n, scale?)
Rounds towards zero at the given scale.
round down(5.5)
round down(-5.5)
round half up
round half up(n, scale?)
Rounds to the nearest value; ties round away from zero (the rounding most people learn in school).
round half up(5.5)
round half up(-5.5)
round half up(2.345, 2)
round half down
round half down(n, scale?)
Rounds to the nearest value; ties round towards zero.
round half down(5.5)
round half down(-5.5)
round half down(2.5)
abs
abs(n)
Returns the absolute value of a number or duration.
abs(-10)
abs(duration("-PT5H"))
abs("x")
modulo
modulo(dividend, divisor)
Returns the remainder of the division. The result has the sign of the divisor.
The result takes the sign of the divisor, as in floored division, not of the dividend as with the % operator in Java or JavaScript. Decimal operands work too.
modulo(12, 5)
modulo(-12, 5)
modulo(12, -5)
modulo(10.5, 3)
modulo(10, 0)
sqrt
sqrt(number)
Returns the square root; null for negative input.
sqrt(16)
sqrt(-1)
sqrt(0)
log
log(number)
Returns the natural logarithm.
decimal(log(10), 4)
log(0)
log(-1)
exp
exp(number)
Returns e raised to the given power.
decimal(exp(5), 4)
exp(0)
odd
odd(number)
Returns true if the number is an odd integer.
odd(5)
odd(2)
odd(null)
even
even(number)
Returns true if the number is an even integer.
even(5)
even(2)
even(0)
even(-2)
number
number(from, grouping separator?, decimal separator?)
Parses a string into a number, optionally describing which grouping and decimal separators the string uses.
Without separator arguments only a plain number is accepted: an optional sign, digits, a dot as the decimal mark and an optional exponent. Thousands separators make the string invalid unless you name them.
number("1 000,5", " ", ",")
number("1250.75")
number("1,000.5", ",", ".")
number("1e3")
number("12abc")
How B-FEEL differs
In standard FEEL an invalid argument makes the result null. The business-friendly
B-FEEL dialect returns a type default instead. Both columns were evaluated by the same engine:
| Expression | FEEL | B-FEEL |
|---|---|---|
| 1 + null | null | 1 |
| 1/0 | null | 0 |
| "5" * 2 | null | 10 |
| modulo(10, 0) | null | 0 |
More FEEL guides
FEEL string functions · FEEL list functions · FEEL date and time functions · Language constructs · Examples cheat sheet
Defined by the OMG DMN 1.6 specification, chapter 10.3.4 (built-in functions).