FEEL

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

FunctionSignatureReturns
decimaldecimal(n, scale)Rounds n to the given scale using banker's rounding (round half to even)
floorfloor(n, scale?)Rounds towards negative infinity at the given scale
ceilingceiling(n, scale?)Rounds towards positive infinity at the given scale
round upround up(n, scale?)Rounds away from zero at the given scale
round downround down(n, scale?)Rounds towards zero at the given scale
round half upround half up(n, scale?)Rounds to the nearest value; ties round away from zero (the rounding most people learn in school)
round half downround half down(n, scale?)Rounds to the nearest value; ties round towards zero
absabs(n)Returns the absolute value of a number or duration
modulomodulo(dividend, divisor)Returns the remainder of the division
sqrtsqrt(number)Returns the square root; null for negative input
loglog(number)Returns the natural logarithm
expexp(number)Returns e raised to the given power
oddodd(number)Returns true if the number is an odd integer
eveneven(number)Returns true if the number is an even integer
numbernumber(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
0.1 + 0.2 = 0.3

Decimal arithmetic: no binary floating-point error.

1 / 3
0.3333333333333333333333333333333333 Try it ↑

34 significant digits.

1000000000000000000000 + 1
1000000000000000000001 Try it ↑

Large integers stay exact.

1 = "1"

Number and string cannot be compared, so the result is null, not false.

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.

-2 ** 2

Read as (-2) ** 2.

-(2 ** 2)

Parenthesize to get the mathematical reading.

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.

Each cell is the function applied to the row value with scale 0.
valuedecimalround half upround half downround upround downfloorceiling
2.52323223
3.54434334
-2.5-2-3-2-3-2-3-2
2.513333223
-2.49-2-2-2-3-2-3-2
decimal(2.345, 2)

Half to even: the 4 stays.

round half up(2.345, 2)

School rounding.

decimal(123.456, -1)

A negative scale rounds to tens, hundreds and so on.

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("1.5")

Strings are not converted. Use number() first.

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)
decimal(123.456, -1)

Negative scale rounds left of the decimal point.

decimal(1.5, null)

The scale is required.

floor

floor(n, scale?)

Rounds towards negative infinity at the given scale.

floor(1.5)
floor(-1.56, 1)
floor(-1.5)

Towards negative infinity, so negative numbers move away from zero.

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 up(1.001)

Any fraction rounds away from zero.

round down

round down(n, scale?)

Rounds towards zero at the given scale.

round down(5.5)
round down(-5.5)
round down(1.999)

Truncates: everything after the scale is dropped.

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 up(-2.5)

Half rounds away from zero, for negative numbers too.

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)
round half down(2.51)

Only an exact half rounds down.

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)

Sign of the divisor: positive.

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)
sqrt(2)
1.414213562373095048801688724209698 Try it ↑

34 significant digits, like every FEEL number.

log

log(number)

Returns the natural logarithm.

decimal(log(10), 4)
2.3026 Try it ↑
log(0)
log(-1)

exp

exp(number)

Returns e raised to the given power.

decimal(exp(5), 4)
148.4132 Try it ↑
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", " ", ",")
1000.5 Try it ↑
number("1250.75")
1250.75 Try it ↑
number("1,000")

A grouping separator has to be declared.

number("1,000.5", ",", ".")
1000.5 Try it ↑
number("1.000,5", ".", ",")
1000.5 Try it ↑

German and Swiss style.

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:

ExpressionFEELB-FEEL
1 + nullnull1
1/0null0
"5" * 2null10
modulo(10, 0)null0

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).