Skip to content

Commit

Permalink
Merge pull request #555 from mpusz/new_format
Browse files Browse the repository at this point in the history
docs: new formatting syntax ideas
  • Loading branch information
mpusz authored Apr 19, 2024
2 parents 6ad831b + 7249144 commit 41f500e
Show file tree
Hide file tree
Showing 16 changed files with 464 additions and 406 deletions.
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

0 comments on commit 41f500e

Please sign in to comment.