Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: new formatting syntax ideas #555

Merged
merged 8 commits into from
Apr 19, 2024
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ int main()
std::cout << std::setw(10) << std::setfill('*') << v2 << '\n'; // ***70 mi/h
std::cout << std::format("{:*^10}\n", v3); // *110 km/h*
std::println("{:%N in %U of %D}", v4); // 70 in mi/h of LT⁻¹
std::println("{:{%N:.2f}%?%U}", v5); // 30.56 m/s
std::println("{:{%N:.2f}%?{%U:dn}}", v6); // 31.29 m⋅s⁻¹
std::println("{::N[.2f]}", v5); // 30.56 m/s
std::println("{::N[.2f]U[dn]}", v6); // 31.29 m⋅s⁻¹
std::println("{:%N}", v7); // 31
}
```
Expand Down
4 changes: 2 additions & 2 deletions docs/blog/posts/2.2.0-released.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,9 @@ quantity q = (90. * km / h).in(mph);
std::cout << "Number: " << q.numerical_value_in(mph) << "\n";
std::cout << "Unit: " << q.unit << "\n";
std::cout << "Dimension: " << q.dimension << "\n";
std::println("{:{%N:.2f}%?%U}", q);
std::println("{::N[.2f]}", q);
std::println("{:.4f} in {} of {}", q.numerical_value_in(mph), q.unit, q.dimension);
std::println("{:{%N:.4f} in %U of %D}", q);
std::println("{:%N in %U of %D:N[.4f]}", q);
```

```text
Expand Down
8 changes: 4 additions & 4 deletions docs/getting_started/look_and_feel.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ performed without sacrificing accuracy. Please see the below example for a quick
std::cout << std::setw(10) << std::setfill('*') << v2 << '\n'; // ***70 mi/h
std::cout << std::format("{:*^10}\n", v3); // *110 km/h*
std::println("{:%N in %U of %D}", v4); // 70 in mi/h of LT⁻¹
std::println("{:{%N:.2f}%?%U}", v5); // 30.56 m/s
std::println("{:{%N:.2f}%?{%U:dn}}", v6); // 31.29 m⋅s⁻¹
std::println("{::N[.2f]}", v5); // 30.56 m/s
std::println("{::N[.2f]U[dn]}", v6); // 31.29 m⋅s⁻¹
std::println("{:%N}", v7); // 31
}
```
Expand Down Expand Up @@ -144,8 +144,8 @@ performed without sacrificing accuracy. Please see the below example for a quick
std::cout << std::setw(10) << std::setfill('*') << v2 << '\n'; // ***70 mi/h
std::cout << std::format("{:*^10}\n", v3); // *110 km/h*
std::println("{:%N in %U of %D}", v4); // 70 in mi/h of LT⁻¹
std::println("{:{%N:.2f}%?%U}", v5); // 30.56 m/s
std::println("{:{%N:.2f}%?{%U:dn}}", v6); // 31.29 m⋅s⁻¹
std::println("{::N[.2f]}", v5); // 30.56 m/s
std::println("{::N[.2f]U[dn]}", v6); // 31.29 m⋅s⁻¹
std::println("{:%N}", v7); // 31
}
```
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ The library source code is hosted on [GitHub](https://github.com/mpusz/mp-units)
int main()
{
constexpr quantity dist = 364.4 * smoot;
std::println("Harvard Bridge length = {:{%N:.5} %U} ({:{%N:.5} %U}, {:{%N:.5} %U}) ± 1 εar",
std::println("Harvard Bridge length = {::N[.5]} ({::N[.5]}, {::N[.5]}) ± 1 εar",
dist, dist.in(usc::foot), dist.in(si::metre));
}
```
Expand All @@ -58,7 +58,7 @@ The library source code is hosted on [GitHub](https://github.com/mpusz/mp-units)
int main()
{
constexpr quantity dist = 364.4 * smoot;
std::println("Harvard Bridge length = {:{%N:.5} %U} ({:{%N:.5} %U}, {:{%N:.5} %U}) ± 1 εar",
std::println("Harvard Bridge length = {::N[.5]} ({::N[.5]}, {::N[.5]}) ± 1 εar",
dist, dist.in(usc::foot), dist.in(si::metre));
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ constexpr auto speed_of_light_in_vacuum = 1 * si::si2019::speed_of_light_in_vacu

QuantityOf<isq::permittivity_of_vacuum> auto q = 1 / (permeability_of_vacuum * pow<2>(speed_of_light_in_vacuum));

std::println("permittivity of vacuum = {} = {:{%N:.3e} %U}", q, q.in(F / m));
std::println("permittivity of vacuum = {} = {::N[.3e]}", q, q.in(F / m));
```

The above first prints the following:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Here is a simple example showing how to deal with such quantities:
const quantity duration = 2 * h;
const quantity speed = avg_speed(distance, duration);

std::println("A car driving {} in {} has an average speed of {:{%N:.4} %U} ({:{%N:.4} %U})",
std::println("A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})",
distance, duration, speed, speed.in(km / h));
}
```
Expand Down Expand Up @@ -103,7 +103,7 @@ Here is a simple example showing how to deal with such quantities:
const quantity duration = 2 * h;
const quantity speed = avg_speed(distance, duration);

std::println("A car driving {} in {} has an average speed of {:{%N:.4} %U} ({:{%N:.4} %U})",
std::println("A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})",
distance, duration, speed, speed.in(km / h));
}
```
Expand Down Expand Up @@ -194,7 +194,7 @@ The previous example can be re-typed using typed quantities in the following way
const quantity duration = isq::time(2 * h);
const quantity speed = avg_speed(distance, duration);

std::println("A car driving {} in {} has an average speed of {:{%N:.4} %U} ({:{%N:.4} %U})",
std::println("A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})",
distance, duration, speed, speed.in(km / h));
}
```
Expand Down Expand Up @@ -223,7 +223,7 @@ The previous example can be re-typed using typed quantities in the following way
const quantity duration = isq::time(2 * h);
const quantity speed = avg_speed(distance, duration);

std::println("A car driving {} in {} has an average speed of {:{%N:.4} %U} ({:{%N:.4} %U})",
std::println("A car driving {} in {} has an average speed of {::N[.4]} ({::N[.4]})",
distance, duration, speed, speed.in(km / h));
}
```
Expand Down
119 changes: 50 additions & 69 deletions docs/users_guide/framework_basics/text_output.md
Original file line number Diff line number Diff line change
Expand Up @@ -529,20 +529,19 @@ std::println("{:d}", kg * m2 / s2); // kg⋅m²/s²
### Quantity formatting

```bnf
quantity-format-spec ::= [fill-and-align] [width] [quantity-specs]
quantity-format-spec ::= [fill-and-align] [width] [quantity-specs] [defaults-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 '}'>
conversion-spec ::= '%' placement-type
placement-type ::= subentity-id | '?' | '%'
defaults-specs ::= ':' default-spec-list
default-spec-list ::= default-spec
default-spec-list default-spec
default-spec ::= subentity-id '[' format-spec ']'
subentity-id ::= 'N' | 'U' | 'D'
format-spec ::= <as specified by the formatter for the argument type>
```

In the above grammar:
Expand All @@ -556,12 +555,9 @@ In the above grammar:
- '?' 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.
- `defaults-specs` token allows overwriting defaults for the underlying formatters with the custom
format string. Each override starts with a subentity identifier ('N', 'U', or 'D') followed by
the format string enclosed in square brackets.

#### Default formatting

Expand All @@ -573,7 +569,6 @@ This is why the following code lines produce the same output:
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
Expand Down Expand Up @@ -610,7 +605,7 @@ Thanks to the grammar provided above, the user can easily decide to either:
- provide custom formatting for components:

```cpp
std::println("Speed: {:{%N:.2f} {%U:n}}", 100. * km / (3 * h));
std::println("Speed: {::N[.2f]U[n]}", 100. * km / (3 * h));
```

```text
Expand All @@ -630,25 +625,11 @@ Thanks to the grammar provided above, the user can easily decide to either:
- 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.
by the user in the `N` defaults specification.

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
Expand All @@ -657,8 +638,8 @@ 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:%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
std::println("{0},{0::N[+]},{0::N[-]},{0::N[ ]}", 1 * m); // 1 m,+1 m,1 m, 1 m
std::println("{0},{0::N[+]},{0::N[-]},{0::N[ ]}", -1 * m); // -1 m,-1 m,-1 m,-1 m
```

where:
Expand All @@ -672,54 +653,54 @@ where:
`precision` token is allowed only for floating-point representation types:

```cpp
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
std::println("{::N[.0]}", 1.2345 * m); // 1 m
std::println("{::N[.1]}", 1.2345 * m); // 1 m
std::println("{::N[.2]}", 1.2345 * m); // 1.2 m
std::println("{::N[.3]}", 1.2345 * m); // 1.23 m
std::println("{::N[.0f]}", 1.2345 * m); // 1 m
std::println("{::N[.1f]}", 1.2345 * m); // 1.2 m
std::println("{::N[.2f]}", 1.2345 * m); // 1.23 m
```

`type` specifies how a value of the representation type is being printed.
For integral types:

```cpp
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
std::println("{::N[b]}", 42 * m); // 101010 m
std::println("{::N[B]}", 42 * m); // 101010 m
std::println("{::N[d]}", 42 * m); // 42 m
std::println("{::N[o]}", 42 * m); // 52 m
std::println("{::N[x]}", 42 * m); // 2a m
std::println("{::N[X]}", 42 * m); // 2A m
```

The above can be printed in an alternate version thanks to the `#` token:

```cpp
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
std::println("{::N[#b]}", 42 * m); // 0b101010 m
std::println("{::N[#B]}", 42 * m); // 0B101010 m
std::println("{::N[#o]}", 42 * m); // 052 m
std::println("{::N[#x]}", 42 * m); // 0x2a m
std::println("{::N[#X]}", 42 * m); // 0X2A m
```

For floating-point values, the `type` token works as follows:

```cpp
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
std::println("{::N[a]}", 1.2345678 * m); // 1.3c0ca2a5b1d5dp+0 m
std::println("{::N[.3a]}", 1.2345678 * m); // 1.3c1p+0 m
std::println("{::N[A]}", 1.2345678 * m); // 1.3C0CA2A5B1D5DP+0 m
std::println("{::N[.3A]}", 1.2345678 * m); // 1.3C1P+0 m
std::println("{::N[e]}", 1.2345678 * m); // 1.234568e+00 m
std::println("{::N[.3e]}", 1.2345678 * m); // 1.235e+00 m
std::println("{::N[E]}", 1.2345678 * m); // 1.234568E+00 m
std::println("{::N[.3E]}", 1.2345678 * m); // 1.235E+00 m
std::println("{::N[g]}", 1.2345678 * m); // 1.23457 m
std::println("{::N[g]}", 1.2345678e8 * m); // 1.23457e+08 m
std::println("{::N[.3g]}", 1.2345678 * m); // 1.23 m
std::println("{::N[.3g]}", 1.2345678e8 * m); // 1.23e+08 m
std::println("{::N[G]}", 1.2345678 * m); // 1.23457 m
std::println("{::N[G]}", 1.2345678e8 * m); // 1.23457E+08 m
std::println("{::N[.3G]}", 1.2345678 * m); // 1.23 m
std::println("{::N[.3G]}", 1.2345678e8 * m); // 1.23E+08 m
```
4 changes: 2 additions & 2 deletions docs/users_guide/framework_basics/the_affine_space.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ room_temp room_ref{};
room_temp room_low = room_ref - number_of_steps * step_delta;
room_temp room_high = room_ref + number_of_steps * step_delta;

std::println("Room reference temperature: {} ({}, {:{%N:.2f}%?%U})\n",
std::println("Room reference temperature: {} ({}, {::N[.2f]})\n",
room_ref.quantity_from_zero(),
room_ref.in(usc::degree_Fahrenheit).quantity_from_zero(),
room_ref.in(si::kelvin).quantity_from_zero());
Expand All @@ -501,7 +501,7 @@ std::println("| {:<18} | {:^18} | {:^18} | {:^18} |",
std::println("|{0:=^20}|{0:=^20}|{0:=^20}|{0:=^20}|", "");

auto print_temp = [&](std::string_view label, auto v) {
std::println("| {:<18} | {:^18} | {:^18} | {:^18{%N:.2f}%?%U} |", label,
std::println("| {:<14} | {:^18} | {:^18} | {:^18:N[.2f]} |", label,
v - room_reference_temp, (v - si::ice_point).in(deg_C), (v - si::absolute_zero).in(deg_C));
};

Expand Down
8 changes: 4 additions & 4 deletions example/clcpp_response.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ void calcs_comparison()
const auto L1A = 2.f * fm;
const auto L2A = 3.f * fm;
const auto LrA = L1A + L2A;
std::cout << MP_UNITS_STD_FMT::format("{:{%N:.30} %U}\n + {:{%N:.30} %U}\n = {:{%N:.30} %U}\n\n", L1A, L2A, LrA);
std::cout << MP_UNITS_STD_FMT::format("{::N[.30]}\n + {::N[.30]}\n = {::N[.30]}\n\n", L1A, L2A, LrA);

std::cout << "The single unit method must convert large\n"
"or small values in other units to the base unit.\n"
Expand All @@ -127,17 +127,17 @@ void calcs_comparison()
const auto L1B = L1A.in(m);
const auto L2B = L2A.in(m);
const auto LrB = L1B + L2B;
std::cout << MP_UNITS_STD_FMT::format("{:{%N:.30e} %U}\n + {:{%N:.30e} %U}\n = {:{%N:.30e} %U}\n\n", L1B, L2B, LrB);
std::cout << MP_UNITS_STD_FMT::format("{::N[.30e]}\n + {::N[.30e]}\n = {::N[.30e]}\n\n", L1B, L2B, LrB);

std::cout << "In multiplication and division:\n\n";

const quantity<isq::area[square(fm)], float> ArA = L1A * L2A;
std::cout << MP_UNITS_STD_FMT::format("{:{%N:.30} %U}\n * {:{%N:.30} %U}\n = {:{%N:.30} %U}\n\n", L1A, L2A, ArA);
std::cout << MP_UNITS_STD_FMT::format("{::N[.30]}\n * {::N[.30]}\n = {::N[.30]}\n\n", L1A, L2A, ArA);

std::cout << "similar problems arise\n\n";

const quantity<isq::area[m2], float> ArB = L1B * L2B;
std::cout << MP_UNITS_STD_FMT::format("{:{%N:.30e} %U}\n * {:{%N:.30e} %U}\n = {:{%N:.30e} %U}\n\n", L1B, L2B, ArB);
std::cout << MP_UNITS_STD_FMT::format("{::N[.30e]}\n * {::N[.30e]}\n = {::N[.30e]}\n\n", L1B, L2B, ArB);
}

} // namespace
Expand Down
Loading
Loading