[boost][decimal] Mungo's review
Boost Decimal Review - Mungo Gill Firstly thank you to Matt for all the effort put into this sizable and clearly well thought out library. Here is my review of the Decimal library. - Background to this review I have used various floating point libraries in the past, mostly in financial applications, and am reasonably familiar with IEEE 754 for binary floating point, although I am less familiar with decimal floating point. I spent about a day writing small test programs, attempting to use it in my own code and stepping through function calls with the debugger. - Building and using I found the library relatively easy to download and use, the only exceptions being Windows ARM and assumptions made regarding fmtlib and format library availability (below). - - Compilation failures on Windows ARM When I tried to compile this on a Windows ARM (64-bit) box (using cmake and MSVC 2022), it failed due to the following error: …boost\decimal\detail\u256.hpp(691): error C3861: '_addcarry_u64': identifier not found (note: arm and arm64 windows probably need to be treated as a special case in the macro checks found in config.hpp lines 88-12. Note that on MSVC ARM the macro __ARM_NEON__ is not defined but arm_neon.h is provided as a header) - - fmtlib library support I tried building a program dependent on decimal on Windows (x64 built with cmake, MSVC 2022, C++20) and encountered an interesting issue: If the fmtlib library happens to be installed on the system then decimal attempts to #include the fmtlib library headers, encountering the following static_assert in fmt/base.h: static_assert(!FMT_UNICODE || use_utf8, "Unicode support requires compiling with /utf-8"); This occurred even though I had made no attempt (by this stage of testing) to write any code dependent upon either fmtlib or std::format. The checks in decimal/fmt_format.hpp should perhaps be tightened so that windows programs compiled without /utf-8 and which do not themselves use the fmtlib library do not give a compilation error. - - format library support The checks for whether or not the <format> header is available checks compiler versions. However, it is feasible that people are using a different standard library from the one you are assuming based on the compiler version (for example clang compilation may or may not have the -stdlib=libc++ flag, or someone might be using a modern clang with an older GNU libstdc++ lacking <format>). It would perhaps be more reliable to check for the __cpp_lib_format feature test macro that C++20 provides. - Points already mentioned by others in the mailing list As these have already been mentioned on the mailing list, I kept these points into their own section. - - Implicit conversions It is slightly confusing having implicit integer construction, but explicit floating point construction. Consider making all construction explicit. Although this leads to slightly more verbose user code, there is less opportunity for unintentioned conversions. Also confusing is the existence of a constructor taking a bool - for example the following code counter-intuitively prints 1 rather than the expected 3. I cannot personally imagine a good use case for wanting to implicitly convert a bool to a decimal. struct MyFloatType { double v_ = 3.14; operator int() const { return static_cast<int>(v_); } }; int main(int argc, char** argv) { MyFloatType f; boost::decimal::decimal64_t g(f); std::cout << g << std::endl; return 0; } - - Rounding I’m also not sure about the rounding convention being a global setting, and I’m not convinced thread-local overrides solve the problem. Perhaps some key functions could take an optional argument to override the global rounding setting ? - Other thoughts on the interface/design I thought this was a nice clean design, which covered most of what I would want covered by a decimal library. - - format library support The std::format support is somewhat lacking in flexibility, for example things like padding character specification (see https://en.cppreference.com/w/cpp/utility/format/spec.html) is not supported. (although I do appreciate that most standard library implementations don’t currently do the unicode width calculations correctly anyway, and supporting them would perhaps be excessive). Note: I did not do the same tests with the fmtlib library. - - locale support The level of locale support is undocumented. I would have included this in my documentation feedback but when I did a test I found that the implementation is also incomplete - i.e. it only appears to impact the decimal point specifier. Here is a simple reproducer (both output lines should be the same): std::locale a("en_US.utf8"); std::cout.imbue(a); std::cout << 1122.89 << std::endl; constexpr boost::decimal::decimal_fast64_t val4{ 112289, -2 }; std::cout << val4 << std::endl; - - cohort-aware functions Although I think the standard arithmetic operations and i/o should behave as they currently designed, there should be facilities to better exploit the fact that decimals can have different cohorts (for example facilities to add/subtract/multiply/divide where the resulting cohort is predictable and deterministic, albeit the preconditions would need to be quite tight to meet such requirements). A use of this would be to output a member of a cohort in a unique manner such that, when parsed, results in a decimal of the same cohort. - Other thoughts on the implementation Generally a good implementation, with clean, easy-to-follow code. I was able to step into operations using the debugger and did not find myself completely confused (although more code comments would always help :-) ) - - internal naming (this is an unduly picky point I know) The 32-bit, 64-bit and 128-bit implementations are called mul_impl, d64_mul_impl and d128_mul_impl. Perhaps for consistency the 32-bit should be d32_mul_impl. More notably, all 3 have comments talking about 128-bit arithmetic which looks to have been copied and pasted but which is not applicable to all 3. Similar points hold true for the division implementations. - - testing Although I understand the argument put forward in the mailing list that testing of special functions exercises the numerical precision, “interface testing” is quite light. For example a test that attempted to use a std::set<decimal32_t> would have quickly found the hashing issue mentioned in the mailing list. - Other thoughts on documentation Again, generally good, easy to read and understand documentation. Coming to this library for the first time I was quickly able to get up to speed. However, I did find myself having to look at the source code or the comments therein to find out how particular methods worked. - - Type conversions I may have missed it in the documentation, but the conversion rules between decimal types should be more clearly specified if they are not already (eg a decimal64_t can be converted to decimal128_fast_t but not to decimal32_fast_t). Also the documentation should specify the type promotion rules. For example, I failed to find in the documentation in the following code? constexpr boost::decimal::decimal_fast64_t val1{ 314, -2 }; constexpr boost::decimal::decimal64_t val2{ 2, 0 }; auto val3 = val1 * val2; - Conclusion My view would be that we should accept this library. Overall the documentation could be improved, and some useful features are missing, but in its current form (assuming the bugs and build issues mentioned above and in the mailing list are addressed) it is in a usable state and would be a useful addition to Boost.
- - Compilation failures on Windows ARM
I thought I had already added win-arm runners to GHA, but apparently not.
- - fmtlib library support
The removal of {fmt} from automatic inclusion is cycling in a PR now since Reuben brought this up as well in his review.
It is slightly confusing having implicit integer construction, but explicit floating point construction. Consider making all construction explicit.
Yes, I think that's been the majority opinion so far.
- - locale support
Will fix.
My view would be that we should accept this library.
Overall the documentation could be improved, and some useful features are missing, but in its current form (assuming the bugs and build issues mentioned above and in the mailing list are addressed) it is in a usable state and would be a useful addition to Boost.
Thank you for your detailed review and comments. Matt
One thing I forgot to mention: when building with cmake, targets should also be generated for the example executables (either by default or by setting an appropriate option). This will make it easier for users to step through them in the debugger to learn how the library works. From: Matt Borland via Boost <boost@lists.boost.org> Date: Monday, 13 October 2025 at 13:20 To: mungo.gill@me.com <mungo.gill@me.com> Cc: Boost developers' mailing list <boost@lists.boost.org>, John Maddock <jz.maddock@googlemail.com>, Matt Borland <matt@mattborland.com> Subject: [boost] Re: [boost][decimal] Mungo's review
- - Compilation failures on Windows ARM
I thought I had already added win-arm runners to GHA, but apparently not.
- - fmtlib library support
The removal of {fmt} from automatic inclusion is cycling in a PR now since Reuben brought this up as well in his review.
It is slightly confusing having implicit integer construction, but explicit floating point construction. Consider making all construction explicit.
Yes, I think that's been the majority opinion so far.
- - locale support
Will fix.
My view would be that we should accept this library.
Overall the documentation could be improved, and some useful features are missing, but in its current form (assuming the bugs and build issues mentioned above and in the mailing list are addressed) it is in a usable state and would be a useful addition to Boost.
Thank you for your detailed review and comments. Matt
One thing I forgot to mention: when building with cmake, targets should also be generated for the example executables (either by default or by setting an appropriate option). This will make it easier for users to step through them in the debugger to learn how the library works.
Did you use -DBUILD_TESTING=ON? I am using Peter's boost_test_jamfile machinery within the library's test/CMakeLists.txt. Within Clion I see all the examples as targets this way. Matt
------- Forwarded Message ------- From: mungo.gill@me.com <mungo.gill@me.com> Date: On Monday, October 13th, 2025 at 1:53 PM Subject: [boost][decimal] Mungo's review To: Boost developers' mailing list <boost@lists.boost.org> CC: Matt Borland <matt@mattborland.com>, John Maddock <jz.maddock@googlemail.com>
Boost Decimal Review - Mungo Gill
Firstly thank you to Matt for all the effort put into this sizable and clearly well thought out library. Here is my review of the Decimal library.
- Background to this review
I have used various floating point libraries in the past, mostly in financial applications, and am reasonably familiar with IEEE 754 for binary floating point, although I am less familiar with decimal floating point.
I spent about a day writing small test programs, attempting to use it in my own code and stepping through function calls with the debugger.
- Building and using
I found the library relatively easy to download and use, the only exceptions being Windows ARM and assumptions made regarding fmtlib and format library availability (below).
- - Compilation failures on Windows ARM
When I tried to compile this on a Windows ARM (64-bit) box (using cmake and MSVC 2022), it failed due to the following error:
…boost\decimal\detail\u256.hpp(691): error C3861: '_addcarry_u64': identifier not found
(note: arm and arm64 windows probably need to be treated as a special case in the macro checks found in config.hpp lines 88-12. Note that on MSVC ARM the macro __ARM_NEON__ is not defined but arm_neon.h is provided as a header)
- - fmtlib library support
I tried building a program dependent on decimal on Windows (x64 built with cmake, MSVC 2022, C++20) and encountered an interesting issue:
If the fmtlib library happens to be installed on the system then decimal attempts to #include the fmtlib library headers, encountering the following static_assert in fmt/base.h:
static_assert(!FMT_UNICODE || use_utf8,
"Unicode support requires compiling with /utf-8");
This occurred even though I had made no attempt (by this stage of testing) to write any code dependent upon either fmtlib or std::format. The checks in decimal/fmt_format.hpp should perhaps be tightened so that windows programs compiled without /utf-8 and which do not themselves use the fmtlib library do not give a compilation error.
- - format library support
The checks for whether or not the <format> header is available checks compiler versions. However, it is feasible that people are using a different standard library from the one you are assuming based on the compiler version (for example clang compilation may or may not have the -stdlib=libc++ flag, or someone might be using a modern clang with an older GNU libstdc++ lacking <format>).
It would perhaps be more reliable to check for the __cpp_lib_format feature test macro that C++20 provides.
- Points already mentioned by others in the mailing list
As these have already been mentioned on the mailing list, I kept these points into their own section.
- - Implicit conversions
It is slightly confusing having implicit integer construction, but explicit floating point construction. Consider making all construction explicit. Although this leads to slightly more verbose user code, there is less opportunity for unintentioned conversions.
Also confusing is the existence of a constructor taking a bool - for example the following code counter-intuitively prints 1 rather than the expected 3. I cannot personally imagine a good use case for wanting to implicitly convert a bool to a decimal.
struct MyFloatType {
double v_ = 3.14;
operator int() const {
return static_cast<int>(v_);
}
};
int main(int argc, char** argv)
{
MyFloatType f;
boost::decimal::decimal64_t g(f);
std::cout << g << std::endl;
return 0;
}
- - Rounding
I’m also not sure about the rounding convention being a global setting, and I’m not convinced thread-local overrides solve the problem. Perhaps some key functions could take an optional argument to override the global rounding setting ?
- Other thoughts on the interface/design
I thought this was a nice clean design, which covered most of what I would want covered by a decimal library.
- - format library support
The std::format support is somewhat lacking in flexibility, for example things like padding character specification (see https://en.cppreference.com/w/cpp/utility/format/spec.html) is not supported.
(although I do appreciate that most standard library implementations don’t currently do the unicode width calculations correctly anyway, and supporting them would perhaps be excessive).
Note: I did not do the same tests with the fmtlib library.
- - locale support
The level of locale support is undocumented. I would have included this in my documentation feedback but when I did a test I found that the implementation is also incomplete - i.e. it only appears to impact the decimal point specifier. Here is a simple reproducer (both output lines should be the same):
std::locale a("en_US.utf8");
std::cout.imbue(a);
std::cout << 1122.89 << std::endl;
constexpr boost::decimal::decimal_fast64_t val4{ 112289, -2 };
std::cout << val4 << std::endl;
- - cohort-aware functions
Although I think the standard arithmetic operations and i/o should behave as they currently designed, there should be facilities to better exploit the fact that decimals can have different cohorts (for example facilities to add/subtract/multiply/divide where the resulting cohort is predictable and deterministic, albeit the preconditions would need to be quite tight to meet such requirements). A use of this would be to output a member of a cohort in a unique manner such that, when parsed, results in a decimal of the same cohort.
- Other thoughts on the implementation
Generally a good implementation, with clean, easy-to-follow code. I was able to step into operations using the debugger and did not find myself completely confused (although more code comments would always help :-) )
- - internal naming (this is an unduly picky point I know)
The 32-bit, 64-bit and 128-bit implementations are called mul_impl, d64_mul_impl and d128_mul_impl. Perhaps for consistency the 32-bit should be d32_mul_impl.
More notably, all 3 have comments talking about 128-bit arithmetic which looks to have been copied and pasted but which is not applicable to all 3.
Similar points hold true for the division implementations.
- - testing
Although I understand the argument put forward in the mailing list that testing of special functions exercises the numerical precision, “interface testing” is quite light. For example a test that attempted to use a std::set<decimal32_t> would have quickly found the hashing issue mentioned in the mailing list.
- Other thoughts on documentation
Again, generally good, easy to read and understand documentation. Coming to this library for the first time I was quickly able to get up to speed. However, I did find myself having to look at the source code or the comments therein to find out how particular methods worked.
- - Type conversions
I may have missed it in the documentation, but the conversion rules between decimal types should be more clearly specified if they are not already (eg a decimal64_t can be converted to decimal128_fast_t but not to decimal32_fast_t).
Also the documentation should specify the type promotion rules. For example, I failed to find in the documentation in the following code?
constexpr boost::decimal::decimal_fast64_t val1{ 314, -2 };
constexpr boost::decimal::decimal64_t val2{ 2, 0 };
auto val3 = val1 * val2;
- Conclusion
My view would be that we should accept this library.
Overall the documentation could be improved, and some useful features are missing, but in its current form (assuming the bugs and build issues mentioned above and in the mailing list are addressed) it is in a usable state and would be a useful addition to Boost.
Apparently Mungo's review is in the moderation queue so here it is for context since my response has already been posted. Matt
participants (2)
-
Matt Borland -
mungo.gill@me.com