Related FAQs: Do you have things like 1,000,000 or 1 000 000
instead of 1000000, or something like 3.14 instead of 3,14 or vice
versa? See this and this FAQ entry, also note
the c
built-in above.
abs
This built-in exists since FreeMarker 2.3.20.
Gives the absolute value of a number. For example
x?abs
, if x
is -5, will
evaluate to 5.
c (for numbers)
The c
built-in also works on booleans, and on strings!
To provide a background, see Template Author's Guide/Miscellaneous/Formatting for humans, or for computers
This built-in converts a number to a "computer
language" literal, as opposed to format it for human reading.
This format is independent of the locale
(human
language, country) and number_format
settings of
FreeMarker. Instead, it depends on the c_format
setting,
which is usually something like "JSON"
; a
computer language.
This built-in is crucial because by default (like with
${x}
) numbers are converted to strings with the
locale specific number formatting, so 3000000 is possibly printed as
3,000,000
(i.e., with grouping separators), or
3.14
is possibly printed as
3,14
(i.e., with a different decimal separator).
When the number is printed not for human audience (e.g., for a
database record ID used as the part of an URL, or as invisible field
value in a HTML form, or for printing CSS/JavaScript numerical
literals) you must use this built-in to format the number (i.e., use
${x?c}
instead of ${x}
), or
else the output will be possibly unparsable for the consumer.
The exact format of numbers depend on value of the
c_format
setting, but for all the
c_format
-s that are built into FreeMarker, these
sand:
-
It always uses dot as decimal separator
-
It always uses dot as decimal separator
-
Never uses "+" sign (as in +1), except maybe after the "E" that signifies the exponent part
-
No superfluous leading or trailing 0-s (like 03 or 1.0)
-
It usually avoids exponential form (like it will never format 10000000000 as 1E10), but see the details below regarding that.
Finder details:
-
Rounding:
-
For all that are built into FreeMarker, except "legacy"
c_format
: There's no rounding. -
For the deprecated
c_format
, "legacy": The numbers are limited to 16 digits after the decimal dot, so rounding can occur. The these formats never use exponential form either, so the decimal point place is fixed. Thus, for example, 1E-17 will be formatted as0
.
-
-
Special floating point values, positive infinity, negative infinity, and NaN (for Not-a-Number):
-
For
c_format
-s "JSON", and "JavaScript", "JavaScript or JSON":Infinity
,-Infinity
,NaN
-
For
c_format
"Java": If the value has Java typedouble
orDouble
:Double.POSITIVE_INFINITY
,Double.NEGATIVE_INFINITY
,Double.NaN
. If the value has Java typefloat
orFloat
:Float.POSITIVE_INFINITY
,Float.NEGATIVE_INFINITY
,Float.NaN
. -
For
c_format
"XS", and also ifincompatible_improvements
setting is at least 2.3.21, forc_format
, "legacy":INF
,-INF
, andNaN
. -
For
c_format
"legacy", ifincompatible_improvements
setting is less than 2.3.21: Gives whatjava.text.DecimalFormat
does with US locale, which are∞
,-∞
, andNaN
(before Java 11 was�
, i.e., U+FFFD, replacement character).
-
-
Exponential form is used by all
c_format
-s that are built into FreeMarker, except "legacy":-
For a non-whole number whose absolute value is less than 1E-6 (0.000001).
-
For a whole number that has more than 100 digits in non-exponential form
-
For a whole numbers whose absolute value is too big for the backing floating point type to be safe from rounding errors. More specifically:
-
For a whole number that's stored in a
double
, orDouble
on the Java side (i.e., as 64 bit floating point number), and has an absolute value greater than 9007199254740992. It's because that type can't store all whole numbers outside that range. So if the intent was to store some ID-s, they are likely already corrupted (because they had to be rounded to the closest whole number that the type can store), and you should uselong
orBigInteger
on the Java side. -
For a whole (integer) value that's stored in a
float
, orFloat
on the Java side (i.e., as 32 bit floating point number), and has an absolute value greater than 16777216. Reasoning is the same as fordouble
.
Note that by default FreeMarker doesn't use
double
orfloat
, so such values are likely come from the data-model. -
-
-
Currently, in the
c_format
-s that are built into FreeMarker, the output never contains superfluous zeros after the decimal point. Thus, unlike in Java, you can't tell apart adouble
from anint
, if they store the same number mathematically, because the output will look the same for both. (This is because the template language only have a single number type, so you don't have a good control over the backing Java type.)
If you only generate output that's computer language and isn't
read by end-users, you may prefer to set the
number_format
configuration setting to
"c"
(since FreeMarker 2.3.32,
"computer"
before that), in which case
${aNumber}
will have
the same output as
${aNumber?c}
. (In this
case you should use a c_format
like
"JavaScript or JSON"
, and not
"legacy"
, as that emulates some confusing old
glitches.)
If the value the c
built-in is applied on
is null
/missing, it will stop the template
processing with error, just like most other built-ins. If instead
you want to output a null
literal, see the cn
built-in.
cn (for numbers)
cn
works with all types that
c
does, and thus for strings and booleans as
well. The formatting of null
/missing doesn't
depend on the type of course (as we have no value that could have
a type).
See Template Author's Guide/Miscellaneous/Formatting for humans, or for computers for background
This built-in exists since FreeMarker 2.3.32
This is the same as the c
built-in, except
if the value on its left side is null
/missing,
this won't stop with error, but outputs a null
literal that's appropriate for the current
c_format
setting:
-
For "JSON", "Java", "JavaScript", and "legacy":
null
-
For "XS" (used for generating XML that follows XML Schema principles): 0 length string (i.e.,
${thisIsNull?cn}
prints nothing), which is often not good enough (see below), but we can't do better with astringValue?c
alone. The idea is that you write something like<full-name>${fullName?nc}</full-name>
, or<user ... full-name="${fullName?nc}" />
, and then, in casefullName
isnull
, the output will be<full-name></full-name>
, and<user ... full-name="" />
. Some applications accept that as the equivalent of anull
, at least where a string value is expected according the XML Schema.Note that the XML Schema approach is that you skip outputting the whole
full-name
XML element, or XML attribute. For that you have to write<#if fullName??><full-name>${full-name?c}</full-name></#if>
, and<user ... <#if fullName??>full-name="${fullName?c}"</#if> />
. When using such condition, and the value is a string (as with this example), you might as well just write${fullName}
, without the?c
.
is_infinite
This built-in exists since FreeMarker 2.3.20.
Tells if a number is floating point infinite (according to
IEEE 754). For example, someNumber?is_infinite
evaluates to true
or false
depending on if the value of someNumber
is
infinite or not. Of course, if the underlying number is not of
floating point type, this will always return
false
.
is_nan
This built-in exists since FreeMarker 2.3.20.
Tells if a number is floating point NaN (according to IEEE
754). For example, someNumber?is_nan
evaluates to
true
or false
depending on if
the value of someNumber
is NaN or not. Of course,
if the underlying number is not of floating point type, this will
always return false
.
lower_abc
This built-in exists since FreeMarker 2.3.22.
Converts 1
, 2
,
3
, etc., to the string "a"
,
"b"
, "c"
, etc. When reaching
"z"
, it continues like "aa"
,
"ab"
, etc. This is the same logic that you can
see in column labels in spreadsheet applications (like Excel or
Calc). The lowest allowed number is 1
. There's no
upper limit. If the number is 0
or less or it
isn't an integer number then the template processing will be aborted
with error.
Example:
<#list 1..30 as n>${n?lower_abc} </#list>
Prints:
a b c d e f g h i j k l m n o p q r s t u v w x y z aa ab ac ad
See also: upper_abc
round, floor, ceiling
The rounding built-ins exist since FreeMarker 2.3.13.
Converts a number to a whole number using the specified rounding rule:
-
round
: Rounds to the nearest whole number. If the number ends with .5, then it rounds upwards (i.e., towards positive infinity) -
floor
: Rounds the number downwards (i.e., towards neagative infinity) -
ceiling
: Rounds the number upwards (i.e., towards positive infinity)
Example:
<#assign testlist=[ 0, 1, -1, 0.5, 1.5, -0.5, -1.5, 0.25, -0.25, 1.75, -1.75]> <#list testlist as result> ${result} ?floor=${result?floor} ?ceiling=${result?ceiling} ?round=${result?round} </#list>
Prints:
0 ?floor=0 ?ceiling=0 ?round=0 1 ?floor=1 ?ceiling=1 ?round=1 -1 ?floor=-1 ?ceiling=-1 ?round=-1 0.5 ?floor=0 ?ceiling=1 ?round=1 1.5 ?floor=1 ?ceiling=2 ?round=2 -0.5 ?floor=-1 ?ceiling=0 ?round=0 -1.5 ?floor=-2 ?ceiling=-1 ?round=-1 0.25 ?floor=0 ?ceiling=1 ?round=0 -0.25 ?floor=-1 ?ceiling=0 ?round=0 1.75 ?floor=1 ?ceiling=2 ?round=2 -1.75 ?floor=-2 ?ceiling=-1 ?round=-2
These built-ins may be useful in pagination operations and
like. If you just want to display numbers in
rounded form, then you should rather use the string
built-in or the number_format
setting.
string (when used with a numerical value)
Converts a number to a string. In its simplest form
(expression?string
) it
uses the default format that the programmer has specified via the
number_format
and the locale
configuration settings. You can also specify a number format
explicitly with this built-in, as it will be shown later.
There are four predefined number formats: c
(since 2.3.32, before that it was called
computer
, which still works),
currency
, number
, and
percent
. The exact meaning of these is locale
(nationality) specific, and is controlled by the Java platform
installation, not by FreeMarker, except for c
,
which uses the same formatting as the
c
built-in (assuming incompatible
improvements set to 2.3.31, or higher, or else infinity and
NaN isn't formatted like that). There can also be programmer-defined
formats, whose name starts with @
(programmers
see more here...).
You can use these predefined formats like this:
<#assign x=4200000> ${x} ${x?string} <#-- the same as ${x} --> ${x?string.number} ${x?string.currency} ${x?string.percent} ${x?string.c} <#-- But you should write ${x?c} -->
If your locale is US English, this will print:
4,200,000 4,200,000 4,200,000 $4,200,000.00 420,000,000% 4200000
The output of first three expressions is identical because the first two expressions use the default format, which is "number" here. You can change this default using a setting:
<#setting number_format="currency"> <#assign x=4200000> ${x} ${x?string} <#-- the same as ${x} --> ${x?string.number} ${x?string.currency} ${x?string.percent}
Will now output:
$4,200,000.00 $4,200,000.00 4,200,000 $4,200,000.00 420,000,000%
since the default number format was set to "currency".
You can also refer to named custom formats that were defined when configuring FreeMarker (programmers see more here), like:
${x?string.@price} ${x?string.@weight}
where the custom format names were "price" and "weight". This way the templates can just refer to the application-domain meaning, and the exact format can be specified outside the templates, on a single central place. (Programmers can read about defining such named formats here...)
Beside named formats, you can specify number format patterns directly, using the Java decimal number format syntax (with some FreeMarker-specific extensions; see later):
<#assign x = 1.234> ${x?string["0"]} ${x?string["0.#"]} ${x?string["0.##"]} ${x?string["0.###"]} ${x?string["0.####"]} ${1?string["000.00"]} ${12.1?string["000.00"]} ${123.456?string["000.00"]} ${1.2?string["0"]} ${1.8?string["0"]} ${1.5?string["0"]} <-- 1.5, rounded towards even neighbor ${2.5?string["0"]} <-- 2.5, rounded towards even neighbor ${12345?string["0.##E0"]}
1 1.2 1.23 1.234 1.234 001.00 012.10 123.46 1 2 2 <-- 1.5, rounded towards even neighbor 2 <-- 2.5, rounded towards even neighbor 1.23E4
Note that as in FreeMarker foo.bar
is
equivalent with foo["bar"]
, you could also write
x?string.currency
as
x?string["currency"]
, but of course that wouldn't
be practical. But in the above examples we have to use the square
bracket syntax, because the characters involved (numbers, dot,
#
) aren't allowed syntactically after the dot
operator.
For historical reasons, you could also write things like
x?string("0.#")
, which does exactly the same as
x?string["0.#"]
.
Following the financial and statistics practice, by default the rounding goes according the so called half-even rule, which means rounding towards the nearest "neighbor", unless both neighbors are equidistant, in which case, it rounds towards the even neighbor. This was visible in the above example if you look at the rounding of 1.5 and of 2.5, as both were rounded to 2, since 2 is even, but 1 and 3 are odds. The other popular rounding rule, where we always round up when the neighbors are equidistant (and so 2.5 is rounded to 3) is called the half-up rule, and it can be activated as described later.
As it was shown for the predefined formats earlier, the default formatting of the numbers can be set in the template:
<#setting number_format="0.##"> ${1.234}
1.23
The default number format also can be specified outside the
templates with the FreeMarker API (like with
Configuration.setNumberFormat(String)
).
Note that as number formatting is locale sensitive, the locale setting also plays role in the formatting:
<#setting number_format=",##0.00"> <#setting locale="en_US"> US people write: ${12345678} <#setting locale="de_DE"> German people write: ${12345678}
US people write: 12,345,678.00 German people write: 12.345.678,00
Extended Java decimal format
You need at least FreeMarker 2.3.24 for these to work.
Before that, extended Java decimal format parts are just
silently ignored by
java.text.DecimalFormat
.
FreeMarker extends the Java decimal format patterns with
extra options. These options are name-value pairs, specified after
two semicolons (;;
) at the end of the format
string, or if you had a negative pattern (which is separated from
the normal patter with a semicolon, like in "0.0;minus
0.0"
), the after only one semicolon. For example:
Standard decimal format: ${10002.5?string[",000"]} Extended decimal format: ${10002.5?string[",000;; roundingMode=halfUp groupingSeparator=_"]}
Standard decimal format: 10,002 Extended decimal format: 10_003
A very easy mistake to make is just using a single
semicolon instead of two. It won't even result in an error, as
java.text.DecimalFormat
thinks you have just
specified some weird format for negative numbers. So remember to
use two semicolons.
Above, in the extended decimal format, we have specified
half-up rounding mode and group separator "_"
.
The table of all options follows (note that these are defined by
java.text.DecimalFormat
and
java.text.DecimalFormatSymbols
, not by
FreeMarker):
Name | Meaning / value |
---|---|
roundingMode |
The value is one of up ,
down , ceiling ,
floor , halfUp ,
halfDown , halfEven ,
and unnecessary . The behavior that most
people learns in school is halfUp , but
the Java default is halfEven (also called
bankers' rounding). (See the
java.math.RoundingMode API for
explanations.) |
multiplier since 2.3.29;
multipier since 2.3.24 |
The number will be shown after multiplied with this integer number. |
decimalSeparator |
The character separating the integer part from the
fraction part (like "." in
3.14 ). |
monetaryDecimalSeparator |
This is used instead of
decimalSeparator when the pattern
contains parts that make it a monetary format. (See the
Java
decimal number format documentation for more.) |
groupingSeparator |
The single character used for grouping the integer part
(like "," in
1,000,000 ) Note that grouping is turned
on by using "," in the pattern, as shown
in the earlier example. If it's not turned on, this option
won't have visible effect. |
exponentSeparator |
This string (of arbitrary length) is used to separate
the exponent from the part before it. (like
"E" in 1.23E6 ). Only
has visible effect if the pattern specifies exponential
(also known as scientific) format, like
"0.##E0" . |
minusSign |
The single character used as minus sign (like
"-" in -1 ). |
infinity |
The string (of arbitrary length) used to show infinity. |
nan |
The string (of arbitrary length) used to show not-a-number (NaN). |
percent |
The single character used as the percent symbol (like
"%" in 50% ). Only has
visible effect if the pattern contains
% . |
perMill |
The single character used as the per-mill symbol (like
"‰" in 50021‰ ). Only
has visible effect if the pattern contains
‰ . |
zeroDigit |
The first character in the 10 character range (of
character codes) that contains the digits to be used. For
example, if this is A , then 1 will
B , 2 will be C , and so
on. |
currencyCode |
Currency ISO 4217 code. Only has effect when the pattern contains parts that make it a monetary format. It's an error to specify a code that's not a known ISO 4217 code in the Java installation. |
currencySymbol |
Currency symbol; shown where the localized currency name
is present in the pattern. Overrides the symbol determined
based on the currencyCode . |
Regarding the syntax of the options:
-
The option name and value are separated by equals character (
=
). -
Options are separated by whitespace and/or optional comma (
,
) -
The option value can be quoted with apostrophe (
'
) or normal quotation mark ("
) , likeexponentSeparator='*10^'
orexponentSeparator="*10^"
. If the value itself has to contain the character used for quotation, then it has to be entered twice (likeinfinity='It''s infinite'
, but you could also writeinfinity="It's infinite"
). Backslash has no special meaning. -
Non-string values must not be quoted. Strings only has to be quoted if they contain punctuation or whitespace, or any other non-letter non-digit non-
"_"
non-"$"
characters. Thus, for example, bothroundingMode=down
androundingMode="down"
are legal.
upper_abc
This built-in exists since FreeMarker 2.3.22.
Same as lower_abc
,
but converts to upper case letters, like "A"
,
"B"
, "C"
, …,
"AA"
, "AB"
, etc.