docs: "Text Output" chapter updated with the recent formatting changes

This commit is contained in:
Mateusz Pusz
2024-02-27 13:58:06 +01:00
parent 5d041fda2d
commit bcca52e298

View File

@ -1,36 +1,199 @@
# Text Output
Besides providing dimensional analysis and units conversions, the library also tries hard to print
any quantity in the most user-friendly way.
Besides providing dimensional analysis and unit conversions, the library also tries hard to print
any quantity in the most user-friendly way. We can print the entire quantity or its
selected parts (numerical value, unit, or dimension).
!!! note
The library does not provide a text output for quantity points, as printing just a number and
a unit is not enough to adequately describe a quantity point. Often, an additional postfix is
required.
The library does not provide a text output for quantity points. The quantity stored inside
is just an implementation detail of this type. It is a vector from a specific origin.
Without the knowledge of the origin, the vector by itself is useless as we can't determine
which point it describes.
For example, the text output of `42 m` may mean many things and can also be confused with an
output of a regular quantity. On the other hand, printing `42 m AMSL` for altitudes above mean
sea level is a much better solution, but the library does not have enough information to print
it that way by itself.
In the current library design, point origin does not provide any text in its definition.
Even if we could add such information to the point's definition, we would not
know how to output it in the text. There may be many ways to do it. For example, should we
prepend or append the origin part to the quantity text?
For example, the text output of `42 m` for a quantity point may mean many things. It may be
an offset from the mountain top, sea level, or maybe the center of Mars.
Printing `42 m AMSL` for altitudes above mean sea level is a much better solution, but the
library does not have enough information to print it that way by itself.
Please let us know if you have a good idea of how to solve this issue.
## Derived unit symbols generation
## Predefined symbols
The library creates symbols for derived ones based on the provided definitions for base units.
The definitions of dimensions, units, prefixes, and constants require assigning text symbols
for each entity. Those symbols will be composed by the library's framework to express dimensions
and units of derived quantities.
### `unit_symbol_formatting`
=== "Dimensions"
```cpp
inline constexpr struct dim_length : base_dimension<"L"> {} dim_length;
inline constexpr struct dim_mass : base_dimension<"M"> {} dim_mass;
inline constexpr struct dim_time : base_dimension<"T"> {} dim_time;
inline constexpr struct dim_electric_current : base_dimension<"I"> {} dim_electric_current;
inline constexpr struct dim_thermodynamic_temperature : base_dimension<{u8"Θ", "O"}> {} dim_thermodynamic_temperature;
inline constexpr struct dim_amount_of_substance : base_dimension<"N"> {} dim_amount_of_substance;
inline constexpr struct dim_luminous_intensity : base_dimension<"J"> {} dim_luminous_intensity;
```
=== "Units"
```cpp
inline constexpr struct second : named_unit<"s", kind_of<isq::time>> {} second;
inline constexpr struct metre : named_unit<"m", kind_of<isq::length>> {} metre;
inline constexpr struct gram : named_unit<"g", kind_of<isq::mass>> {} gram;
inline constexpr struct kilogram : decltype(kilo<gram>) {} kilogram;
inline constexpr struct newton : named_unit<"N", kilogram * metre / square(second)> {} newton;
inline constexpr struct joule : named_unit<"J", newton * metre> {} joule;
inline constexpr struct watt : named_unit<"W", joule / second> {} watt;
inline constexpr struct coulomb : named_unit<"C", ampere * second> {} coulomb;
inline constexpr struct volt : named_unit<"V", watt / ampere> {} volt;
inline constexpr struct farad : named_unit<"F", coulomb / volt> {} farad;
inline constexpr struct ohm : named_unit<{u8"Ω", "ohm"}, volt / ampere> {} ohm;
```
=== "Prefixes"
```cpp
template<PrefixableUnit auto U> struct micro_ : prefixed_unit<{u8"µ", "u"}, mag_power<10, -6>, U> {};
template<PrefixableUnit auto U> struct milli_ : prefixed_unit<"m", mag_power<10, -3>, U> {};
template<PrefixableUnit auto U> struct centi_ : prefixed_unit<"c", mag_power<10, -2>, U> {};
template<PrefixableUnit auto U> struct deci_ : prefixed_unit<"d", mag_power<10, -1>, U> {};
template<PrefixableUnit auto U> struct deca_ : prefixed_unit<"da", mag_power<10, 1>, U> {};
template<PrefixableUnit auto U> struct hecto_ : prefixed_unit<"h", mag_power<10, 2>, U> {};
template<PrefixableUnit auto U> struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U> {};
template<PrefixableUnit auto U> struct mega_ : prefixed_unit<"M", mag_power<10, 6>, U> {};
```
=== "Constants"
```cpp
inline constexpr struct hyperfine_structure_transition_frequency_of_cs : named_unit<{u8"Δν_Cs", "dv_Cs"}, mag<9'192'631'770> * hertz> {} hyperfine_structure_transition_frequency_of_cs;
inline constexpr struct speed_of_light_in_vacuum : named_unit<"c", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum;
inline constexpr struct planck_constant : named_unit<"h", mag<ratio{662'607'015, 100'000'000}> * mag_power<10, -34> * joule * second> {} planck_constant;
inline constexpr struct elementary_charge : named_unit<"e", mag<ratio{1'602'176'634, 1'000'000'000}> * mag_power<10, -19> * coulomb> {} elementary_charge;
inline constexpr struct boltzmann_constant : named_unit<"k", mag<ratio{1'380'649, 1'000'000}> * mag_power<10, -23> * joule / kelvin> {} boltzmann_constant;
inline constexpr struct avogadro_constant : named_unit<"N_A", mag<ratio{602'214'076, 100'000'000}> * mag_power<10, 23> / mole> {} avogadro_constant;
inline constexpr struct luminous_efficacy : named_unit<"K_cd", mag<683> * lumen / watt> {} luminous_efficacy;
```
!!! important
Two symbols always have to be provided if the primary symbol contains characters outside of
the [basic literal character set](https://en.cppreference.com/w/cpp/language/charset).
The first must be provided as a UTF-8 literal and may contain any Unicode characters.
The second one must provide an alternative spelling and only use characters from within of
[basic literal character set](https://en.cppreference.com/w/cpp/language/charset).
!!! note
Unicode provides only a minimal set of characters available as subscripts, which are often used
to differentiate various constants and quantities of the same kind. To workaround this issue,
**mp-units** uses the '_' character to specify that the following characters should be considered
a subscript of the symbol.
!!! tip
For older compilers, it might be required to specify a `basic_symbol_text` class explicitly
template name to initialize it with two symbols:
```cpp
inline constexpr struct ohm : named_unit<basic_symbol_text{u8"Ω", "ohm"}, volt / ampere> {} ohm;
```
## Symbols for derived entities
### `text_encoding`
[ISQ](../../appendix/glossary.md#isq) and [SI](../../appendix/glossary.md#si) standards always
specify symbols using Unicode encoding. This is why it is a default and primary target for
text output. However, in some applications or environments, a standard ASCII-like text output
using only the characters from the [basic literal character set](https://en.cppreference.com/w/cpp/language/charset)
can be preferred by users.
This is why the library provides an option to change the default encoding to the ASCII one with:
```cpp
enum class text_encoding : std::int8_t {
unicode, // µs; m³; L²MT⁻³
ascii, // us; m^3; L^2MT^-3
default_encoding = unicode
};
```
### Symbols of derived dimensions
#### `dimension_symbol_formatting`
`dimension_symbol_formatting` is a data type describing the configuration of the symbol generation
algorithm.
```cpp
struct dimension_symbol_formatting {
text_encoding encoding = text_encoding::default_encoding;
};
```
#### `dimension_symbol()`
Returns a `std::string_view` with the symbol of a dimension for the provided configuration:
```cpp
template<dimension_symbol_formatting fmt = dimension_symbol_formatting{}, typename CharT = char, Dimension D>
[[nodiscard]] consteval std::string_view dimension_symbol(D);
```
For example:
```cpp
static_assert(dimension_symbol<{.encoding = text_encoding::ascii}>(isq::power.dimension) == "L^2MT^-3");
```
!!! note
`std::string_view` is returned only when C++23 is available. Otherwise, an instance of a
`basic_fixed_string` is being returned.
#### `dimension_symbol_to()`
Inserts the generated dimension symbol into the output text iterator at runtime.
```cpp
template<typename CharT = char, std::output_iterator<CharT> Out, Dimension D>
constexpr Out dimension_symbol_to(Out out, D d, dimension_symbol_formatting fmt = dimension_symbol_formatting{});
```
For example:
```cpp
std::string txt;
dimension_symbol_to(std::back_inserter(txt), isq::power.dimension, {.encoding = text_encoding::ascii});
std::cout << txt << "\n";
```
The above prints:
```text
L^2MT^-3
```
### Symbols of derived units
#### `unit_symbol_formatting`
`unit_symbol_formatting` is a data type describing the configuration of the symbol generation
algorithm. It contains three orthogonal fields, each with a default value.
```cpp
enum class text_encoding : std::int8_t {
unicode, // m³; µs
ascii, // m^3; us
default_encoding = unicode
};
enum class unit_symbol_solidus : std::int8_t {
one_denominator, // m/s; kg m⁻¹ s⁻¹
always, // m/s; kg/(m s)
@ -51,13 +214,20 @@ struct unit_symbol_formatting {
};
```
### `unit_symbol()`
`unit_symbol_solidus` impacts how the division of unit symbols is being presented in the text
output. By default, the '/' will be printed if only one unit component is in the
denominator. Otherwise, the exponent syntax will be used.
`unit_symbol_separator` specifies how multiple multiplied units should be separated from each
other. By default, the space (' ') will be used as a separator.
#### `unit_symbol()`
Returns a `std::string_view` with the symbol of a unit for the provided configuration:
```cpp
template<unit_symbol_formatting fmt = unit_symbol_formatting{}, typename CharT = char, Unit U>
[[nodiscard]] consteval auto unit_symbol(U);
[[nodiscard]] consteval std::string_view unit_symbol(U);
```
For example:
@ -70,12 +240,13 @@ static_assert(unit_symbol<{.solidus = unit_symbol_solidus::never,
!!! note
`std::string_view` is returned only when C++23 is available. Otherwise, an instance of a
`basic_fixed_string` is being returned.
`basic_fixed_string` is being returned. See more in the
[C++ compiler support](../../getting_started/installation_and_usage.md#static-constexpr-variables-in-constexpr-functions)
chapter.
### `unit_symbol_to()`
#### `unit_symbol_to()`
Inserts the generated unit symbol to the output text iterator at runtime based on the provided
configuration.
Inserts the generated unit symbol into the output text iterator at runtime.
```cpp
template<typename CharT = char, std::output_iterator<CharT> Out, Unit U>
@ -97,9 +268,7 @@ The above prints:
kg⋅m⋅s⁻²
```
## Quantity text output
### Customization point
## `space_before_unit_symbol` customization point
The [SI Brochure](../../appendix/references.md#SIBrochure) says:
@ -110,9 +279,12 @@ The [SI Brochure](../../appendix/references.md#SIBrochure) says:
second for plane angle, `°`, `` and ``, respectively, for which no space is left between the
numerical value and the unit symbol.
To support the above, the library exposes `space_before_unit_symbol` customization point. By default,
its value is `true` for all the units, so the space between a number and a unit will be present in the
output text. To change this behavior, we have to provide a partial specialization for a specific unit:
There are more units with such properties. For example, percent (`%`) and per mille(``).
To support the above and other similar cases, the library exposes `space_before_unit_symbol`
customization point. By default, its value is `true` for all the units, so the space between a number
and a unit will be inserted in the output text. To change this behavior, we have to provide a partial
specialization for a specific unit:
```cpp
template<>
@ -121,36 +293,35 @@ inline constexpr bool space_before_unit_symbol<non_si::degree> = false;
!!! note
The above works only for [the default formatting](#default-formatting). In case we provide our
own format specification (e.g., `std::format("{:%Q %q}", q)`), the library will always obey this
specification for all the units (no matter of what is the actual value of the
`space_before_unit_symbol` customization point) and the separating space will always be present
in this case.
The above works only for [the default formatting](#default-formatting) or for the format
strings that use `%?` placement field (`std::format("{}", q)` is equivalent to
`std::format("{:%N%?%U}", q)`).
In case a user provides custom format specification (e.g., `std::format("{:%N %U}", q)`),
the library will always obey this specification for all the units (no matter what the actual
value of the `space_before_unit_symbol` customization point is) and the separating space will always
be used in this case.
### Output streams
## Output streams
!!! tip
The output streaming support is opt-in and can be enabled by including the `<mp-units/ostream.h>`
header file.
The easiest way to print a quantity is to provide its object to the output stream:
The easiest way to print a dimension, unit, or quantity is to provide its object to the output stream:
```cpp
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
using namespace mp_units::international::unit_symbols;
const QuantityOf<isq::speed> auto v1 = avg_speed(220. * km, 2 * h);
const QuantityOf<isq::speed> auto v2 = avg_speed(140. * mi, 2 * h);
std::cout << v1 << '\n'; // 110 km/h
std::cout << v2 << '\n'; // 70 mi/h
std::cout << v1 << '\n'; // 110 km/h
std::cout << v2 << '\n'; // 70 mi/h
std::cout << v2.unit << '\n'; // mi/h
std::cout << v2.dimension << '\n'; // LT⁻¹
```
The text output will always print the [value of a quantity](../../appendix/glossary.md#quantity-value)
typically followed by a space and then the symbol of a [unit](../../appendix/glossary.md#unit)
associated with this quantity.
The text output will always print the value using the default formatting for this entity.
!!! important "Important: Don't assume a unit"
@ -164,11 +335,14 @@ associated with this quantity.
```
#### Output stream formatting
### Output stream formatting
Only basic formatting can be applied to output streams. It includes control over width, fill,
and alignment of the entire quantity and formatting of a quantity numerical value according
to the general C++ rules:
and alignment.
The numerical value of the quantity will be printed according to the current stream state and standard
manipulators may be used to customize that (assuming that the underlying representation type
respects them).
```cpp
std::cout << "|" << std::setw(10) << 123 * m << "|\n"; // | 123 m|
@ -182,94 +356,22 @@ std::cout << "|" << std::setw(10) << std::setfill('*') << 123 * m << "|\n"; //
stream just use `std::cout << std::format(...)`.
### `std::format`
## Text formatting
The library provides custom formatters for `std::format` facility, which allows fine-grained control
over what and how it is being printed in the text output.
!!! tip
The text formatting facility support is opt-in and can be enabled by including the
`<mp-units/format.h>` header file.
The **mp-units** library provides custom formatters for `std::format` facility which allows
fine-grained control over what and how it is being printed in the text output.
### Controlling width, fill, and alignment
#### Grammar
```ebnf
quantity-format-spec ::= [fill-and-align] [width] [quantity-specs]
quantity-specs ::= conversion-spec
quantity-specs conversion-spec
quantity-specs literal-char
literal-char ::= any character other than '{' or '}'
conversion-spec ::= '%' type
type ::= [rep-modifier] 'Q'
[unit-modifier] 'q'
rep-modifier ::= [sign] [#] [precision] [L] [rep-type]
rep-type ::= one of
a A b B d e E f F g G o x X
unit-modifier ::= [text-encoding] [unit-symbol-solidus] [unit-symbol-separator]
[text-encoding] [unit-symbol-separator] [unit-symbol-solidus]
[unit-symbol-solidus] [text-encoding] [unit-symbol-separator]
[unit-symbol-solidus] [unit-symbol-separator] [text-encoding]
[unit-symbol-separator] [text-encoding] [unit-symbol-solidus]
[unit-symbol-separator] [unit-symbol-solidus] [text-encoding]
text-encoding ::= one of
U A
unit-symbol-solidus ::= one of
o a n
unit-symbol-separator ::= one of
s d
```
In the above grammar:
- `fill-and-align`, `width`, `sign`, `#`, `precision`, and `L` tokens, as well as the individual
tokens of `rep-type` are defined in the [format.string.std](https://wg21.link/format.string.std)
chapter of the C++ standard specification,
- tokens `Q` and `q` of `type` are described in the [time.format](https://wg21.link/time.format)
chapter of the C++ standard specification,
- `text-encoding` tokens specify the unit text encoding:
- `U` (default) uses the **Unicode** symbols defined by the [SI](../../appendix/glossary.md#si)
specification (e.g., ``, `µs`)
- `A` token forces non-standard **ASCII**-only output (e.g., `m^3`, `us`)
- `unit-symbol-solidus` tokens specify how the division of units should look like:
- `o` (default) outputs `/` only when there is only **one** unit in the denominator, otherwise
negative exponents are printed (e.g., `m/s`, `kg m⁻¹ s⁻¹`)
- `a` **always** uses solidus (e.g., `m/s`, `kg/(m s)`)
- `n` **never** prints solidus, which means that negative exponents are always used
(e.g., `m s⁻¹`, `kg m⁻¹ s⁻¹`)
- `unit-symbol-separator` tokens specify how multiplied unit symbols should be separated:
- `s` (default) uses **space** as a separator (e.g., `kg m²/s²`)
- `d` uses half-high **dot** (``) as a separator (e.g., `kg⋅m²/s²`)
#### Default formatting
To format `quantity` values, the formatting facility uses `quantity-format-spec`. If left empty,
the default formatting is applied. The same default formatting is also applied to the output streams.
This is why the following code lines produce the same output:
```cpp
std::cout << "Distance: " << 123 * km << "\n";
std::cout << std::format("Distance: {}\n", 123 * km);
std::cout << std::format("Distance: {:%Q %q}\n", 123 * km);
```
!!! note
For some quantities, the `{:%Q %q}` format may provide a different output than the default one.
It will happen, for example for:
- units for which [`space_before_unit_symbol`](#customization-point) customization point is set
to `false`,
- quantities of dimension one with a unit one.
#### Controlling width, fill, and alignment
To control width, fill, and alignment, the C++ standard grammar tokens `fill-and-align` and `width`
are being used, and they treat a quantity value and symbol as a contiguous text:
Formatting grammar for all the entities provides control over width, fill, and alignment. The C++
standard grammar tokens `fill-and-align` and `width` are being used. They treat the entity as
a contiguous text to be aligned. For example, here are a few examples of the quantity numerical
value and symbol formatting:
```cpp
std::println("|{:0}|", 123 * m); // |123 m|
@ -282,6 +384,10 @@ std::println("|{:*>10}|", 123 * m); // |*****123 m|
std::println("|{:*^10}|", 123 * m); // |**123 m***|
```
It is important to note that in the second line above, the quantity text is aligned to
the right by default, which is consistent with the formatting of numeric types. Units and dimensions behave
as text and, thus, are aligned to the left by default.
!!! note
[`std::println` is a C++23 facility](https://en.cppreference.com/w/cpp/io/print). In case we
@ -291,26 +397,268 @@ std::println("|{:*^10}|", 123 * m); // |**123 m***|
std::cout << std::format("<format-string>\n", <format-args>);
```
### Dimension formatting
#### Quantity value, symbol, or both?
The user can easily decide to either print a whole quantity (value and symbol) or only its parts.
Also, a custom style of quantity formatting might be applied:
```cpp
std::println("{:%Q}", 123 * km); // 123
std::println("{:%q}", 123 * km); // km
std::println("{:%Q%q}", 123 * km); // 123km
```bnf
dimension-format-spec ::= [fill-and-align] [width] [dimension-spec]
dimension-spec ::= [text-encoding]
text-encoding ::= 'U' | 'A'
```
In the above grammar:
#### Quantity value formatting
- `fill-and-align` and `width` tokens are defined in the [format.string.std](https://wg21.link/format.string.std)
chapter of the C++ standard specification,
- `text-encoding` token specifies the symbol text encoding:
- `U` (default) uses the **Unicode** symbols defined by [@ISO80000] (e.g., `LT⁻²`),
- `A` forces non-standard **ASCII**-only output (e.g., `LT^-2`).
Dimension symbols of some quantities are specified to use Unicode signs by the
[ISQ](../../appendix/glossary.md#isq) (e.g., `Θ` symbol for the _thermodynamic temperature_
dimension). The library follows this by default. From the engineering point of view, sometimes
Unicode text might not be the best solution, as terminals of many (especially embedded) devices
can output only letters from the basic literal character set. In such a case, the dimension
symbol can be forced to be printed using such characters thanks to `text-encoding` token:
```cpp
std::println("{}", isq::dim_thermodynamic_temperature); // Θ
std::println("{:A}", isq::dim_thermodynamic_temperature); // O
std::println("{}", isq::power.dimension); // L²MT⁻³
std::println("{:A}", isq::power.dimension); // L^2MT^-3
```
### Unit formatting
```bnf
unit-format-spec ::= [fill-and-align] [width] [unit-spec]
unit-spec ::= [text-encoding] [unit-symbol-solidus] [unit-symbol-separator] [L]
[text-encoding] [unit-symbol-separator] [unit-symbol-solidus] [L]
[unit-symbol-solidus] [text-encoding] [unit-symbol-separator] [L]
[unit-symbol-solidus] [unit-symbol-separator] [text-encoding] [L]
[unit-symbol-separator] [text-encoding] [unit-symbol-solidus] [L]
[unit-symbol-separator] [unit-symbol-solidus] [text-encoding] [L]
unit-symbol-solidus ::= '1' | 'a' | 'n'
unit-symbol-separator ::= 's' | 'd'
```
In the above grammar:
- `fill-and-align` and `width` tokens are defined in the [format.string.std](https://wg21.link/format.string.std)
chapter of the C++ standard specification,
- `unit-symbol-solidus` token specifies how the division of units should look like:
- '1' (default) outputs `/` only when there is only **one** unit in the denominator, otherwise
negative exponents are printed (e.g., `m/s`, `kg m⁻¹ s⁻¹`)
- 'a' **always** uses solidus (e.g., `m/s`, `kg/(m s)`)
- 'n' **never** prints solidus, which means that negative exponents are always used
(e.g., `m s⁻¹`, `kg m⁻¹ s⁻¹`)
- `unit-symbol-separator` token specifies how multiplied unit symbols should be separated:
- 's' (default) uses **space** as a separator (e.g., `kg m²/s²`)
- 'd' uses half-high **dot** (``) as a separator (e.g., `kg⋅m²/s²`) (requires the Unicode encoding)
- 'L' is reserved for possible future localization use in case the C++ standard library gets access to
the ICU-like database.
!!! note
The above grammar intended that the elements of `unit-spec` can appear in
any order as they have unique characters. Users shouldn't have to remember the order of those tokens
to control the formatting of a unit symbol.
Unit symbols of some quantities are specified to use Unicode signs by the [SI](../../appendix/glossary.md#si)
(e.g., `Ω` symbol for the _resistance_ quantity). The library follows this by default. From the
engineering point of view, Unicode text might not be the best solution sometimes, as terminals
of many (especially embedded) devices can output only letters from the basic literal character set.
In such a case, the unit symbol can be forced to be printed using such characters thanks to
`text-encoding` token:
```cpp
std::println("{}", si::ohm); // Ω
std::println("{:A}", si::ohm); // ohm
std::println("{}", us); // µs
std::println("{:A}", us); // us
std::println("{}", m / s2); // m/s²
std::println("{:A}", m / s2); // m/s^2
```
Additionally, both ISO 80000 and [SI](../../appendix/glossary.md#si) leave some freedom on how to
print unit symbols. This is why two additional tokens were introduced.
`unit-symbol-solidus` specifies how the division of units should look like. By default,
`/` will be used only when the denominator contains only one unit. However, with the 'a' or 'n'
options, we can force the facility to print the `/` character always (even when there are more units
in the denominator), or never, in which case a parenthesis will be added to enclose all denominator
units.
```cpp
std::println("{}", m / s); // m/s
std::println("{}", kg / m / s2); // kg m⁻¹ s⁻²
std::println("{:a}", m / s); // m/s
std::println("{:a}", kg / m / s2); // kg/(m s²)
std::println("{:n}", m / s); // m s⁻¹
std::println("{:n}", kg / m / s2); // kg m⁻¹ s⁻²
```
Also, there are a few options to separate the units being multiplied. ISO 80000 (part 1) says:
!!! quote "ISO 80000-1"
When symbols for quantities are combined in a product of two or more quantities, this combination
is indicated in one of the following ways: `ab`, `a b`, `a · b`, `a × b`
_NOTE 1_ In some fields, e.g., vector algebra, distinction is made between `a ∙ b` and `a × b`.
The library supports `a b` and `a · b` only. Additionally, we decided that the extraneous space
in the latter case makes the result too verbose, so we decided just to use the `·` symbol as
a separator.
!!! note
Please let us know if you require more formatting options here.
The `unit-symbol-separator` token allows us to obtain the following outputs:
```cpp
std::println("{}", kg * m2 / s2); // kg m²/s²
std::println("{:d}", kg * m2 / s2); // kg⋅m²/s²
```
!!! note
'd' requires the Unicode encoding to be set.
### Quantity formatting
```bnf
quantity-format-spec ::= [fill-and-align] [width] [quantity-specs]
quantity-specs ::= conversion-spec
quantity-specs conversion-spec
quantity-specs literal-char
literal-char ::= <any character other than '{', '}', or '%'>
conversion-spec ::= placement-spec
subentity-replacement-field
placement-spec ::= '%' placement-type
placement-type ::= 'N' | 'U' | 'D' | '?' | '%'
subentity-replacement-field ::= '{' '%' subentity-id [format-specifier] '}'
subentity-id ::= literal-char
subentity-id literal-char
format-specifier ::= ':' format-spec
format-spec ::= <as specified by the formatter for the argument type; cannot start with '}'>
```
In the above grammar:
- `fill-and-align` and `width` tokens are defined in the [format.string.std](https://wg21.link/format.string.std)
chapter of the C++ standard specification,
- `placement-type` token specifies which entity should be put and where:
- 'N' inserts a default-formatted numerical value of the quantity,
- 'U' inserts a default-formatted unit of the quantity,
- 'D' inserts a default-formatted dimension of the quantity,
- '?' inserts an optional separator between the number and a unit based on the value of
`space_before_unit_symbol` for this unit,
- '%' just inserts '%' character.
- `subentity-replacement-field` token allows the composition of formatters. The following identifiers
are recognized by the quantity formatter:
- 'N' passes `format-spec` to the `formatter` specialization for the quantity representation
type,
- 'U' passes `format-spec` to the `formatter` specialization for the unit type,
- 'D' passes `format-spec` to the `formatter` specialization for the dimension type.
#### Default formatting
To format `quantity` values, the formatting facility uses `quantity-format-spec`. If left empty,
the default formatting is applied. The same default formatting is also applied to the output streams.
This is why the following code lines produce the same output:
```cpp
std::cout << "Distance: " << 123 * km << "\n";
std::cout << std::format("Distance: {}\n", 123 * km);
std::cout << std::format("Distance: {:%N%?%U}\n", 123 * km);
std::cout << std::format("Distance: {:{%N}%?{%U}}\n", 123 * km);
```
!!! note
For some quantities, the `{:%N %U}` format may provide a different output than the default one,
as some units have `space_before_unit_symbol` customization point explicitly set to `false`
(e.g., `%` and `°`).
#### Quantity numerical value, unit symbol, or both?
Thanks to the grammar provided above, the user can easily decide to either:
- print a whole quantity:
```cpp
std::println("Speed: {}", 120 * km / h);
```
```text
Speed: 120 km/h
```
- provide custom quantity formatting:
```cpp
std::println("Speed: {:%N in %U}", 120 * km / h);
```
```text
Speed: 120 in km/h
```
- provide custom formatting for components:
```cpp
std::println("Speed: {:{%N:.2f} {%U:n}}", 100. * km / (3 * h));
```
```text
Speed: 33.33 km h⁻¹
```
- print only specific components (numerical value, unit, or dimension):
```cpp
std::println("Speed:\n- number: {0:%N}\n- unit: {0:%U}\n- dimension: {0:%D}", 120 * km / h);
```
```text
Speed:
- number: 120
- unit: km/h
- dimension: LT⁻¹
```
!!! note
The above grammar allows repeating the same field many times, possibly with a different
format spec. For example:
```cpp
std::println("Speed: {:%N {%N:.4f} {%N:.2f} {%U:n}}", 100. * km / (3 * h));
```
```text
Speed: 33.333333333333336 33.3333 33.33 km h⁻¹
```
#### Formatting of the quantity numerical value
The representation type used as a numerical value of a quantity must provide its own formatter
specialization. It will be called by the quantity formatter with the format-spec provided
by the user in the `%N` replacement field.
In case we use C++ fundamental arithmetic types with our quantities the standard formatter
specified in [format.string.std](https://wg21.link/format.string.std) will be used. The rest
of this chapter assumes that it is the case and provides some usage examples.
`sign` token allows us to specify how the value's sign is being printed:
```cpp
std::println("{0:%Q %q},{0:%+Q %q},{0:%-Q %q},{0:% Q %q}", 1 * m); // 1 m,+1 m,1 m, 1 m
std::println("{0:%Q %q},{0:%+Q %q},{0:%-Q %q},{0:% Q %q}", -1 * m); // -1 m,-1 m,-1 m,-1 m
std::println("{0:%N %U},{0:{%N:+} %U},{0:{%N:-} %U},{0:{%N: } %U}", 1 * m); // 1 m,+1 m,1 m, 1 m
std::println("{0:%N %U},{0:{%N:+} %U},{0:{%N:-} %U},{0:{%N: } %U}", -1 * m); // -1 m,-1 m,-1 m,-1 m
```
where:
@ -324,111 +672,54 @@ where:
`precision` token is allowed only for floating-point representation types:
```cpp
std::println("{:%.0Q %q}", 1.2345 * m); // 1 m
std::println("{:%.1Q %q}", 1.2345 * m); // 1.2 m
std::println("{:%.2Q %q}", 1.2345 * m); // 1.23 m
std::println("{:{%N:.0} %U}", 1.2345 * m); // 1 m
std::println("{:{%N:.1} %U}", 1.2345 * m); // 1 m
std::println("{:{%N:.2} %U}", 1.2345 * m); // 1.2 m
std::println("{:{%N:.3} %U}", 1.2345 * m); // 1.23 m
std::println("{:{%N:.0f} %U}", 1.2345 * m); // 1 m
std::println("{:{%N:.1f} %U}", 1.2345 * m); // 1.2 m
std::println("{:{%N:.2f} %U}", 1.2345 * m); // 1.23 m
```
`rep-type` specifies how a value of the representation type is being printed.
`type` specifies how a value of the representation type is being printed.
For integral types:
```cpp
std::println("{:%bQ %q}", 42 * m); // 101010 m
std::println("{:%BQ %q}", 42 * m); // 101010 m
std::println("{:%dQ %q}", 42 * m); // 42 m
std::println("{:%oQ %q}", 42 * m); // 52 m
std::println("{:%xQ %q}", 42 * m); // 2a m
std::println("{:%XQ %q}", 42 * m); // 2A m
std::println("{:{%N:b} %U}", 42 * m); // 101010 m
std::println("{:{%N:B} %U}", 42 * m); // 101010 m
std::println("{:{%N:d} %U}", 42 * m); // 42 m
std::println("{:{%N:o} %U}", 42 * m); // 52 m
std::println("{:{%N:x} %U}", 42 * m); // 2a m
std::println("{:{%N:X} %U}", 42 * m); // 2A m
```
The above can be printed in an alternate version thanks to the `#` token:
```cpp
std::println("{:%#bQ %q}", 42 * m); // 0b101010 m
std::println("{:%#BQ %q}", 42 * m); // 0B101010 m
std::println("{:%#oQ %q}", 42 * m); // 052 m
std::println("{:%#xQ %q}", 42 * m); // 0x2a m
std::println("{:%#XQ %q}", 42 * m); // 0X2A m
std::println("{:{%N:#b} %U}", 42 * m); // 0b101010 m
std::println("{:{%N:#B} %U}", 42 * m); // 0B101010 m
std::println("{:{%N:#o} %U}", 42 * m); // 052 m
std::println("{:{%N:#x} %U}", 42 * m); // 0x2a m
std::println("{:{%N:#X} %U}", 42 * m); // 0X2A m
```
For floating-point values, the `rep-type` token works as follows:
For floating-point values, the `type` token works as follows:
```cpp
std::println("{:%aQ %q}", 1.2345678 * m); // 0x1.3c0ca2a5b1d5dp+0 m
std::println("{:%.3aQ %q}", 1.2345678 * m); // 0x1.3c1p+0 m
std::println("{:%AQ %q}", 1.2345678 * m); // 0X1.3C0CA2A5B1D5DP+0 m
std::println("{:%.3AQ %q}", 1.2345678 * m); // 0X1.3C1P+0 m
std::println("{:%eQ %q}", 1.2345678 * m); // 1.234568e+00 m
std::println("{:%.3eQ %q}", 1.2345678 * m); // 1.235e+00 m
std::println("{:%EQ %q}", 1.2345678 * m); // 1.234568E+00 m
std::println("{:%.3EQ %q}", 1.2345678 * m); // 1.235E+00 m
std::println("{:%gQ %q}", 1.2345678 * m); // 1.23457 m
std::println("{:%gQ %q}", 1.2345678e8 * m); // 1.23457e+08 m
std::println("{:%.3gQ %q}", 1.2345678 * m); // 1.23 m
std::println("{:%.3gQ %q}", 1.2345678e8 * m); // 1.23e+08 m
std::println("{:%GQ %q}", 1.2345678 * m); // 1.23457 m
std::println("{:%GQ %q}", 1.2345678e8 * m); // 1.23457E+08 m
std::println("{:%.3GQ %q}", 1.2345678 * m); // 1.23 m
std::println("{:%.3GQ %q}", 1.2345678e8 * m); // 1.23E+08 m
```
#### Unit symbol formatting
Unit symbols of some quantities are specified to use Unicode signs by the
[SI](../../appendix/glossary.md#si) (e.g., `Ω` symbol for the resistance quantity). The **mp-units**
library follows this by default. From the engineering point of view, sometimes Unicode text might
not be the best solution as terminals of many (especially embedded) devices are ASCII-only.
In such a case, the unit symbol can be forced to be printed using ASCII-only characters thanks to
the `text-encoding` token:
```cpp
std::println("{}", 10 * si::ohm); // 10 Ω
std::println("{:%Q %Aq}", 10 * si::ohm); // 10 ohm
std::println("{}", 125 * us); // 125 µs
std::println("{:%Q %Aq}", 125 * us); // 125 us
std::println("{}", 9.8 * (m / s2)); // 9.8 m/s²
std::println("{:%Q %Aq}", 9.8 * (m / s2)); // 9.8 m/s^2
```
Additionally, both [ISQ](../../appendix/glossary.md#isq) and [SI](../../appendix/glossary.md#si)
leave some freedom on how to print unit symbols. This is why two additional tokens were introduced.
`unit-symbol-solidus` specifies how the division of units should look like. By default,
`/` will be used only when the denominator contains only one unit. However, with the `a` or `n`
options, we can force the facility to print the `/` character always (even when there are more units
in the denominator), or never in which case a parenthesis will be added to enclose all denominator
units.
```cpp
std::println("{:%Q %q}", 1 * m / s); // 1 m/s
std::println("{:%Q %q}", 1 * kg / m / s2); // 1 kg m⁻¹ s⁻²
std::println("{:%Q %aq}", 1 * m / s); // 1 m/s
std::println("{:%Q %aq}", 1 * kg / m / s2); // 1 kg/(m s²)
std::println("{:%Q %nq}", 1 * m / s); // 1 m s⁻¹
std::println("{:%Q %nq}", 1 * kg / m / s2); // 1 kg m⁻¹ s⁻²
```
Also, there are a few options to separate the units being multiplied:
!!! quote "ISO 80000-1"
When symbols for quantities are combined in a product of two or more quantities, this combination
is indicated in one of the following ways: `ab`, `a b`, `a · b`, `a × b`
_NOTE 1_ In some fields, e.g., vector algebra, distinction is made between `a ∙ b` and `a × b`.
As of today, the **mp-units** library only supports `a b` and `a · b`. Additionally,
we decided that the extraneous space in the latter case makes the result too verbose, so we decided
to just use the `·` symbol as a separator.
!!! note
Please let us know if you require more formatting options here.
The `unit-symbol-separator` token allows us to obtain the following outputs:
```cpp
std::println("{:%Q %q}", 1 * kg * m2 / s2); // 1 kg m²/s²
std::println("{:%Q %dq}", 1 * kg * m2 / s2); // 1 kg⋅m²/s²
std::println("{:{%N:a} %U}", 1.2345678 * m); // 1.3c0ca2a5b1d5dp+0 m
std::println("{:{%N:.3a} %U}", 1.2345678 * m); // 1.3c1p+0 m
std::println("{:{%N:A} %U}", 1.2345678 * m); // 1.3C0CA2A5B1D5DP+0 m
std::println("{:{%N:.3A} %U}", 1.2345678 * m); // 1.3C1P+0 m
std::println("{:{%N:e} %U}", 1.2345678 * m); // 1.234568e+00 m
std::println("{:{%N:.3e} %U}", 1.2345678 * m); // 1.235e+00 m
std::println("{:{%N:E} %U}", 1.2345678 * m); // 1.234568E+00 m
std::println("{:{%N:.3E} %U}", 1.2345678 * m); // 1.235E+00 m
std::println("{:{%N:g} %U}", 1.2345678 * m); // 1.23457 m
std::println("{:{%N:g} %U}", 1.2345678e8 * m); // 1.23457e+08 m
std::println("{:{%N:.3g} %U}", 1.2345678 * m); // 1.23 m
std::println("{:{%N:.3g} %U}", 1.2345678e8 * m); // 1.23e+08 m
std::println("{:{%N:G} %U}", 1.2345678 * m); // 1.23457 m
std::println("{:{%N:G} %U}", 1.2345678e8 * m); // 1.23457E+08 m
std::println("{:{%N:.3G} %U}", 1.2345678 * m); // 1.23 m
std::println("{:{%N:.3G} %U}", 1.2345678e8 * m); // 1.23E+08 m
```