Skip to content

3. Exact numbers

This chapter measures the specimen as it really is, to a tenth of a millimetre and a tenth of a kilonewton. It shows why checked_evaluate's results are exact, what an exact result looks like when it has no decimal, and how to round one for reading.

The program is chapter 2's, without the second strength quantity, with decimal measurements, and with the strength also printed rounded.

Exact decimal literals

The specimen's sides measure 150.2 mm and 149.8 mm, and it carried 675.4 kN:

auto const specimen = formula::environment(
    formula::Measured<SideA> { 150.2_r }, formula::Measured<SideB> { 149.8_r }, formula::Measured<Load> { 675.4_r });

The _r literal, from formula::literals, makes an exact formula::Rational from its spelling: 150.2_r is 751/5, exactly 150.2, never the double nearest it. checked_evaluate calculates in Rational, a fraction of two 128-bit integers, so nothing is rounded on the way in or during the calculation. A value too large for a Rational is reported as an error, never rounded (Limits). The usual example of binary floating point going wrong holds exactly:

std::println("0.1 + 0.2 == 0.3: {}", 0.1_r + 0.2_r == 0.3_r ? "yes" : "no");

With double, 0.1 + 0.2 == 0.3 is false. With Rational it is true.

An exact result that has no decimal

The formulas and their evaluation are chapter 2's:

auto const area = formula::checked_evaluate(loadedArea, specimen);
if (!area)
{
    std::println("cannot calculate the area: {}", area.error());
    return 1;
}
std::println("{} = {} ({})", formula::symbol_of<Area>(), *area, area->source());

auto const result = formula::checked_evaluate(strength, specimen);
if (!result)
{
    std::println("cannot calculate the strength: {}", result.error());
    return 1;
}
std::println("{} = {} ({})", formula::symbol_of<Strength>(), *result, result->source());

The area, 150.2 mm × 149.8 mm, is 22499.96 mm², which has an exact decimal and prints as one. The strength, 675.4 kN over 22499.96 mm², is 16885000/562499 MPa. That fraction has no terminating decimal, so the result prints as the fraction. A rounded decimal would be a different number, and the library does not print one unless asked to.

Rounding for reading

A reader wants a decimal. The format spec asks for one:

std::println("{}, for reading = {:~.2HalfEven}", formula::symbol_of<Strength>(), *result);

{:~.2HalfEven} names the number of decimal places, 2, and the rounding mode, HalfEven. There is no default mode: the spec always names one. The ~ writes the exact decimal where the value has one, and otherwise rounds it and marks the rounded value with ≈, so it cannot pass for the exact result. The rounding happens only in the text printed; the result itself stays exact. Rounding that is part of a method, where a standard prescribes it, comes in chapter 5.

Output

A_c = 22499.96 mm2 (derived)
f_c = 16885000/562499 MPa (derived)
f_c, for reading = ≈30.02 MPa
0.1 + 0.2 == 0.3: yes

Summary

  • _r -- an exact decimal literal, read from its spelling; in formula::literals.
  • formula::Rational -- an exact fraction of two integers, the number type checked_evaluate calculates in.
  • {:~.NMode} -- formats a value rounded to N places in the rounding mode named and marked ≈; a value with an exact decimal prints unrounded.

Further reading