//////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2010, 2026 Contributors to the Eclipse Foundation
//
// See the NOTICE file(s) distributed with this work for additional
// information regarding copyright ownership.
//
// This program and the accompanying materials are made available
// under the terms of the MIT License which is available at
// https://opensource.org/licenses/MIT
//
// SPDX-License-Identifier: MIT
//////////////////////////////////////////////////////////////////////////////

include::../_part_attributes.asciidoc[]

:leveloffset: -2

[[lang-tut-text-chapter-fmt]]
== Text formatting

indexterm:[text,formatting]
indexterm:[text formatting]
indexterm:[print output,text formatting]
This lesson explains text formatting by means of examples, in the context of <<lang-tut-text-chapter-print,print output>>.
It does not explain all uses of text formatting, nor all details of text formatting.
For those details, see the reference manual's <<lang-ref-text-fmt,text formatting details documentation>> instead.

In this lesson, only the output from print declarations is shown when console output is presented.
All other output, such as that from the CIF simulator, is omitted.

While we explain text formatting in the context of print declarations, text formatting by means of the `fmt` function can also be used to create <<lang-tut-values-chapter-strings,string values>> in other contexts.

indexterm:[text formatting,introduction]

=== Introduction

Consider the following CIF specification:

[source, cif]
----
print "Time=" + <string>time;
----

When simulated, it could give the following output:

[source, console]
----
Time=0.0
Time=3.333333333333336
Time=5.000000000000001
Time=10.0
----

As you can see, due to rounding and simulation imprecision, the length of the textual representations of the values of `time` can vary wildly during simulation.
Furthermore, the explicit casts (conversions) from real to string (`<string>`) and string concatenation (the `pass:c[+]` operator) can quickly become cluttered, and hard to read.

To improve readability of the print declaration, we could adapt the CIF specification to the following:

[source, cif]
----
print fmt("Time=%s", time);
----

Here, manual text construction has been replaced by the use of the `fmt` standard library function.
The first argument is a _format pattern_, and the remaining arguments are the values that are to be included in the text, in this case the value of variable `time`.
The `fmt` function ensures that we no longer have to use casts.
We also no longer have to create pieces of text and concatenate them (using the `pass:c[+]` operator).

This is a typical use of a format pattern.
The print declaration prints the value that results from text formatting.
The value that is used is the value of variable `time`.
The value of this variable is not used 'as is', but is instead converted to a textual representation using the format pattern.
This format pattern specifies that the resulting text should start with `Time=`.
It also specifies that the value (of variable `time`) should be included at the end of the textual representation.
The `%s` part of the format pattern is a _format specifier_.
The `%s` specifier is the generic specifier that can convert any type of value to a textual representation.

When simulated, this new specification gives the exact same output as the previous version.

indexterm:[text formatting,real values]

=== Real value formatting

As we saw in the previous section, due to rounding and simulation imprecision, the length of the generic textual representations of the values of variable `time` can vary wildly during simulation.
This reduces readability of the results, as a lot of irrelevant digits are included.
Consider the following alternative CIF specification:

[source, cif]
----
print fmt("Time=%.2f", time);
----

Here, instead of a `%s` specifier, a `%f` specifier is used.
This specifier can only be used for `real` numbers, and indicates that the number should be formatted as a floating point number in decimal representation.
The `.2` part specifies the precision, and indicates that the floating point number in decimal notation should have exactly two digits after the decimal point.
When simulated, this could give the following output:

[source, console]
----
Time=0.00
Time=3.33
Time=5.00
Time=10.00
----

indexterm:[text formatting,columns]

=== Putting values in columns

In the examples above, we included a single value in the output.
Now consider the following CIF specification:

[source, cif]
----
print fmt("%.2f %.2f %.2f", x, y, z);
----

Here, the values of variables `x`, `y`, and `z` are included in the formatted result.
The format pattern includes each of the values (the first `%.2f` includes the value of variable `x`, the second `%.2f` includes the value of variable `y`, etc).
The format pattern includes spaces between the format specifiers (between the `%.2f` parts), and thus the formatted values include spaces between them as well.
When simulated, this could give the following output:

[source, console]
----
1.50 0.00 -3.57
2.34 3.75 5.78
4.71 12345.34 -3.12
-3.25 1.25 99.20
----

Due to some larger values being mixed with shorter values, as well as due to having both positive and negative values, the output is not so easy to read.
This can be solved by using the following CIF specification instead:

[source, cif]
----
print fmt("%10.2f %10.2f %10.2f", x, y, z);
----

By including a _width_ of `10`, the text is now nicely formatted into columns of ten characters wide:

[source, console]
----
---------- ---------- ----------
      1.50       0.00      -3.57
      2.34       3.75       5.78
      4.71   12345.34      -3.12
     -3.25       1.25      99.20
----

The first line is not actual output, but is included to make it easier to see the columns.
Not only is the output now nicely put in columns, but due to the exactly two digits after the dot (`.`), the dots are now also nicely aligned.

indexterm:[text formatting,large numbers]

=== Large numbers

Consider the following CIF specification:

[source, cif]
----
print fmt("%.2f", time);
----

When simulated, this could give the following output:

[source, console]
----
0.00
1.34
5000.23
2147185402.17
----

As the values of variable `time` get larger and larger, their textual representations become longer and longer, and it becomes more and more difficult to see exactly how large the values are.
The following alternative CIF specification solves this problem:

[source, cif]
----
print fmt("%,.2f", time);
----

The addition of the comma (`,`) _flag_ ensures that we get the following output instead:

[source, console]
----
0.00
1.34
5,000.23
2,147,185,402.17
----

That is, a comma is used in the result as _thousand separator_, making it easier to see that the value of variable `time` is just over two billion, rather than for instance just over 200 million.

indexterm:[text formatting,left alignment]

=== Left alignment

Earlier, we put values in columns.
By default, if a _width_ is used, text is right-justified (aligned to the right).
We can also justify it to the left, as in this CIF specification:

[source, cif]
----
print fmt("%-10.2d %-10.2d %-10.2d", g, h, i);
----

Here the `%d` specifier is used instead of the `%f` specifier.
The `%d` specifier can only be used for integer values, while the `%f` specifier can only be used for real values.
Besides a different specifier, the minus (`-`) flag is added, and different variables are used as values.
After the changes, the CIF specification could result in the following output:

[source, console]
----
---------- ---------- ----------
184        3675       2
19350      29         -2956
-17        -964563    235
2946       567        -25072563
----

Once again, the first line is not actual output, but is included to make it easier to see the columns.
Observe how the addition of the `-` flag resulted in the right-justified output being changed to left-justified (aligned to the left) output.
The presence of negative numbers makes that the left-most digits of the columns are not nicely aligned.
The following CIF specification solves this:

[source, cif]
----
print fmt("%-+10.2d %-+10.2d %-+10.2d", x, y, z);
----

The addition of the plus (`pass:c[+]`) flag means that for non-negative numbers, a plus (`pass:c[+]`) character is always included:

[source, console]
----
---------- ---------- ----------
+184       +3675      +2
+19350     +29        -2956
-17        -964563    +235
+2946      +567       -25072563
----

If preferred, a space can be used instead of a plus, resulting in the following CIF specification:

[source, cif]
----
print fmt("%- 10.2d %- 10.2d %- 10.2d", x, y, z);
----

and the following output:

[source, console]
----
---------- ---------- ----------
 184        3675       2
 19350      29        -2956
-17        -964563     235
 2946       567       -25072563
----

=== More formatting

In this lesson, we've seen a few forms of text formatting using the `fmt` function.
However, CIF supports various other specifiers, besides the `%s`, `%f`, and `%d` specifiers that were used in this lesson.
All those specifiers support various flags, widths, and precisions, and allow them to be combined in various ways.
For the complete details of text formatting, see the reference manual's <<lang-ref-text-fmt,text formatting details documentation>>.
