Formatting Support

Boost.Decimal supports formatting with both <format> (when C++20 and header are both available), and <fmt/format.h> with all language standards.

Format is supported when using C++20 and a compiler with appropriate support: GCC >= 13, Clang >= 18, MSVC >= 19.40

Locale Modifier

If a format string begins with "L" the current global locale will be applied. This can be set as so:

// Locale can be any valid locale that is installed on your system
const char* locale = "de_DE.UTF-8";
const std::locale a(locale);
std::locale::global(a);

Sign Modifier

Modifier

Format

"+"

All values will have a sign

"-"

Only negative values have a sign

" "

Positive values have a leading space, Negative have a minus sign

Type Modifiers

The following type modifiers are the same as those used by built-in floating point values:

Modifier

Format

"g" or "G"

General

"e" or "E"

Scientific

"f"

Fixed

"x" or "X"

Hex

"a" or "A"

Cohort Preserving Scientific

Example usage for scientific format would be: {:e}

The uppercase format will return with all applicable values in uppercase (e.g. 3.14E+02 vs 3.14e+02)

Precision Modifiers

Precision can be specified in the same way as built-in floating point values. For example a scientific format with 3 digits or precision would be: {:.3e}

Padding Modifiers

If you want all values to be printed with a fixed width padding is allowed before the precision modifier. For example with {:10.3e}:

  • 3.14 → " 3.140e+00"

  • 3.141 → " 3.141e+00"

Note the space at the front of these string to keep with width at 10 characters

String Literal Support

If you want the result to be a different string width than char you can specify this with the format string. For example if you want the result to be wchar_t you can use L"{}". wchar_t, char16_t, char32_t are supported from C++17. char8_t is supported from C++23.

std::format only supports char and wchar_t types per the C++ specification.

Putting it All Together

The appropriate order for the full format specifier is:

String literal "{Sign, Padding, Precision, Type, Locale}"

Examples

This example can be found in the example/ folder as fmt_format.cpp

The header <boost/decimal/fmt_format.hpp> is NOT part of the convenience header, because it is an optional dependency on a potentially compiled library.

#include <boost/decimal/decimal32_t.hpp>    // For type decimal32_t
#include <boost/decimal/decimal64_t.hpp>    // For type decimal64_t
#include <boost/decimal/fmt_format.hpp>     // For {fmt} support
#include <iostream>

int main()
{
    constexpr boost::decimal::decimal64_t val1 {"3.14"};
    constexpr boost::decimal::decimal32_t val2 {"3.141"};

    // The easiest is no specification which is general format
    // Given these values they will print in fixed format
    std::cout << "Default Format:\n";
    std::cout << fmt::format("{}", val1) << '\n';
    std::cout << fmt::format("{}", val2) << "\n\n";

    // Next we can add a type modifier to get scientific formatting
    std::cout << "Scientific Format:\n";
    std::cout << fmt::format("{:e}", val1) << '\n';
    std::cout << fmt::format("{:e}", val2) << "\n\n";

    // Next we can add a type modifier to get scientific formatting
    // Here this gives one digit of precision rounded according to current rounding mode
    std::cout << "Scientific Format with Specified Precision:\n";
    std::cout << fmt::format("{:.1e}", val1) << '\n';
    std::cout << fmt::format("{:.1e}", val2) << "\n\n";

    // Width specifies the minimum field width
    // By default, values are right-aligned with space fill
    std::cout << "Width with Default Alignment (right-align, space fill):\n";
    std::cout << fmt::format("[{:12}]", val1) << '\n';
    std::cout << fmt::format("[{:12.3e}]", val2) << "\n\n";

    // Alignment specifiers: < (left), > (right), ^ (center)
    std::cout << "Alignment Specifiers:\n";
    std::cout << fmt::format("[{:<12}]", val1) << " (left)\n";
    std::cout << fmt::format("[{:>12}]", val1) << " (right)\n";
    std::cout << fmt::format("[{:^12}]", val1) << " (center)\n\n";

    // Fill character can be any character placed before the alignment specifier
    std::cout << "Custom Fill Characters:\n";
    std::cout << fmt::format("[{:*<12}]", val1) << " (left, * fill)\n";
    std::cout << fmt::format("[{:->12}]", val1) << " (right, - fill)\n";
    std::cout << fmt::format("[{:=^12}]", val1) << " (center, = fill)\n\n";

    // Combining fill, alignment, sign, width, precision, and type
    std::cout << "Combined Format Specifiers:\n";
    std::cout << fmt::format("[{:*>+12.2e}]", val1) << " (right, * fill, + sign, precision 2, scientific)\n";
    std::cout << fmt::format("[{:-<+12.2f}]", val2) << " (left, - fill, + sign, precision 2, fixed)\n";

    return 0;
}

Output:

Default Format:
3.14
3.141

Scientific Format:
3.14e+00
3.141e+00

Scientific Format with Specified Precision:
3.1e+00
3.1e+00

Width with Default Alignment (right-align, space fill):
[        3.14]
[   3.141e+00]

Alignment Specifiers:
[3.14        ] (left)
[        3.14] (right)
[    3.14    ] (center)

Custom Fill Characters:
[3.14********] (left, * fill)
[--------3.14] (right, - fill)
[====3.14====] (center, = fill)

Combined Format Specifiers:
[***+3.14e+00] (right, * fill, + sign, precision 2, scientific)
[+3.14-------] (left, - fill, + sign, precision 2, fixed)

This same example can be run with <format> by replacing namespaces fmt:: with std::.